I seem to have hit a road block with trying to get a batch file to subtract a set number from a user entered variable. I have a command where a user enters an IP address and then delimits it to four separate octets, then I need to subtract 19 from the last octet. No matter how I set it up all it get back is -19. I read that set /a doesn't need the % but nothing seems to help
echo. Enter IP address of the CSR on the site.
echo. Example: 11.152.34.82
set /p IP="IP Address: "
for /F "tokens=1,2,3,4 delims=." %%a in ("%IP%") do echo %%a, %%b, %%c, %%d
pause
set /a z=d - 19
echo. %z%
pause
I have also tried
set /a z=%%d - 19
echo. %z%
pause
and
set /a z=%d% - 19
echo. %z%
pause
Any help would be appreciated.
Capture the last octet within the loop then operate on that variable:
for /F "tokens=1,2,3,4 delims=." %%a in ("%IP%") do (
echo %%a, %%b, %%c, %%d
set last=%%d
)
set /a z=%last% - 19
echo z=%z%
Using the method similar to the last one I provided in you last question:
#Echo Off
Echo= Enter IP address of the CSR on the site.
Echo= Example: 11.152.34.82
Set/P "IPA= "
Call :Sub %IPA:.= %
Rem Your commands using new %IPA% here:
Echo= Your new IP address is %IPA%
Pause
Exit/B
:Sub
ClS
If %4 Gtr 19 Set/A LO=%4-19
Set "IPA=%1.%2.%3.%LO%"
Everything works fine in this program except when user enters the correct number generated by the program. For example, it replies "too low" if you enter 13, and "too high" if you enter 14. How do you fix this so the 3rd if statement functions properly?
#echo off
title Game
set num1=%random%%101
:start
set /p num2=Enter a number:
if %num2% gtr %num1% (echo too high
pause
cls
goto :start
)
if %num2% lss %num1% (echo too low
pause
cls
goto :start
)
if %num2% equ %num1% (echo Bingo!)
pause
cls
exit
This batch file works fine:
#echo off
title Game
setlocal EnableDelayedExpansion
set /A num1=%random% %% 101
:Retry
cls
set "num2="
set /P "num2=Enter a number (%num1%): "
if !num2! GTR %num1% (
echo too high
pause
goto Retry
)
if !num2! LSS %num1% (
echo too low
pause
goto Retry
)
if !num2! EQU %num1% (
endlocal
echo Bingo!
pause
exit /B
)
goto Retry
The line
set num1=%random%%101
could be also used in the batch file above, but creates
a number with a random part and
appended the first argument passed to the batch file if called with a parameter at all and
appended the digits 0 and 1.
I suppose like SomethingDark that a random number between 0 and 100 is wanted here.
start is a standard Windows command and therefore it is advisable not to use start as label although possible.
Delayed expansion is enabled and used for num2 to avoid an exit of the batch file caused by a syntax error when the batch user enters nothing or by mistake not a number.
Also the environment variable num2 is always unset before prompting the user as otherwise the batch user could just hit RETURN or ENTER to continue with whatever variable num2 currently has as value.
Note: The random number is output in prompt text to see what to enter for testing.
I'm trying to make a script to shutdown my computer.
It asks for input wether the value will be in minutes or hours, in order to convert them to seconds for the shutdown command.
This is what my script looks like and it doesn't work.
#echo off
color 0a
:home
echo Hours (h) or minutes (m)?
set /p unit=
if "%unit%"=="h" (
echo For how long do you want to delay it?
set /p value=
set /a secs="value*=60*60"
) if "%unit%"=="m" (
echo For how long do you want to delay it?
set /p value=
set /a secs="value*=60"
) else (
goto:home
)
rem shutdown /s /f /t %secs%
echo %secs%
echo Closing in 3 seconds.
ping 127.0.0.1 -n 4 > nul
If anyone could lighten me up or come up with a better way to achieve it (which I'm sure exists), it'd be great.
I feel stupid now.
I totally forgot about else if.
Anyways, here's the finished working script. Also, input is now case-insensitive.
#echo off
echo ****************************
echo * *
echo * QUICK SHUTDOWN SCHEDULER *
echo * *
echo ****************************
echo.
echo Hours (H) or minutes (M)?
:home
set /p unit=
if /I "%unit%"=="h" (
echo For how many hours do you want to delay it?
set /p value=
set /a secs="value*=60*60"
) else if /I "%unit%"=="m" (
echo For how many minutes do you want to delay it?
set /p value=
set /a secs="value*=60"
) else (
echo Please only type H or M.
goto:home
)
shutdown /s /f /t %secs%
echo Closing in 3 seconds.
ping 127.0.0.1 -n 4 > nul
You may use choice command to get the input from user instead set /P; this way, you don't even need to check the provided input because it always be correct. For further details type: choice /?.
You may also use an array to show a messsage with the right units name without using an if to identify it; see this post for further details.
#echo off
setlocal EnableDelayedExpansion
rem Define the "units" array
set "units[1]=minutes"
set "units[60]=hours"
choice /N /C MH /M "Hours (H) or minutes (M)? "
set /A unit=(%errorlevel%-1)*59 + 1
set /p "value=For how many !units[%unit%]! do you want to delay it? "
set /a "secs=value* unit *60"
shutdown /s /f /t %secs%
echo Closing in 3 seconds.
ping 127.0.0.1 -n 4 > nul
I am trying to make a countdown timer but the timer doesn't count up or count down. Please Help ! it just shows +1.
:Plus
echo-------------------------------------------------------------------------
echo Please enter starting number...
set /p time=%time%
echo Please enter ending number...
set /p etime=%etime%
echo-------------------------------------------------------------------------
echo.%time%
set time=%time%
:loop1
set /p time=%time%+1
if %time%==%etime% goto timesup1
echo.%time%
ping localhost -n 2 > nul
goto loop1
:timesup1
echo.%etime%
echo Time is Up!
goto Opt
(The Minus Option just shows the number typed then '-1')
:Minus
echo ------------------------------------------------------------------------
echo Please enter starting number...
set /p time=%time%
echo Please enter ending number...
set /p etime=%etime%
echo-------------------------------------------------------------------------
echo %time%
set time=%time%
:loop2
set /p time=%time%-1
if %time%==%etime% goto timesup2
echo %time%
ping localhost -n 2 > nul
goto loop2
:timesup2
echo %etime%
echo Time is Up!
goto Opt
your problem is here:
set /p time=%time%+1
you need /a, not /p to do calculations (see set /?)
Also you shouldn't set %time%, because it's a system variable used by windows. (use another name, like sTime(analogue to eTime)
I would like to show the user with a spinner, that something is done in background but do not know how this works in a batchfile.
Does anyone have a clue?
This can actually be done quite easily with pure native commands, you just have to know how to use the more tricky of them. No use of external tools like VBScript or nasty side effects like clearing the screen are necessary.
What you're looking for is the equivalent of the bash "echo -n" command which outputs a line without the newline. In XP batch, this is achieved by using "set /p" (ask user for response with a prompt) with empty input as follows:
<nul (set /p junk=Hello)
echo. again.
will output the string "Hello again." with no intervening newline.
That trick (and the use of CTRL-H, the backspace character can be seen in the following test script which starts (one after the other) a 10-second sub-task with a 20-second timeout and a 15-second sub-task with a 10-second timeout.
The payload script is created by the actual running script and its only requirement is that it do the work it has to do then delete a flag file when finished, so that the monitor function will be able to detect it.
Keep in mind that the ^H strings in this script are actually CTRL-H characters, the ^| is two separate characters used to escape the pipe symbol.
#echo off
:: Localise environment.
setlocal enableextensions enabledelayedexpansion
:: Specify directories. Your current working directory is used
:: to create temporary files tmp_*.*
set wkdir=%~dp0%
set wkdir=%wkdir:~0,-1%
:: First pass, 10-second task with 20-second timeout.
del "%wkdir%\tmp_*.*" 2>nul
echo >>"%wkdir%\tmp_payload.cmd" ping 127.0.0.1 -n 11 ^>nul
echo >>"%wkdir%\tmp_payload.cmd" del "%wkdir%\tmp_payload.flg"
call :monitor "%wkdir%\tmp_payload.cmd" "%wkdir%\tmp_payload.flg" 20
:: Second pass, 15-second task with 10-second timeout.
del "%wkdir%\tmp_*.*" 2>nul:
echo >>"%wkdir%\tmp_payload.cmd" ping 127.0.0.1 -n 16 ^>nul
echo >>"%wkdir%\tmp_payload.cmd" del "%wkdir%\tmp_payload.flg"
call :monitor "%wkdir%\tmp_payload.cmd" "%wkdir%\tmp_payload.flg" 10
goto :final
:monitor
:: Create flag file and start the payload minimized.
echo >>%2 dummy
start /min cmd.exe /c "%1"
:: Start monitoring.
:: i is the indicator (0=|,1=/,2=-,3=\).
:: m is the number of seconds left before timeout.
set i=0
set m=%3
<nul (set /p z=Waiting for child to finish: ^|)
:: Loop here awaiting completion.
:loop
:: Wait one second.
ping 127.0.0.1 -n 2 >nul
:: Update counters and output progress indicator.
set /a "i = i + 1"
set /a "m = m - 1"
if %i% equ 4 set i=0
if %i% equ 0 <nul (set /p z=^H^|)
if %i% equ 1 <nul (set /p z=^H/)
if %i% equ 2 <nul (set /p z=^H-)
if %i% equ 3 <nul (set /p z=^H\)
:: End conditions, complete or timeout.
if not exist %2 (
echo.
echo. Complete.
goto :final
)
if %m% leq 0 (
echo.
echo. *** ERROR: Timed-out waiting for child.
goto :final
)
goto :loop
:final
endlocal
If you don't mind the screen clearing...try this:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET COUNT=1
START CALC
:BEGIN
CLS
IF !COUNT! EQU 1 ECHO \
IF !COUNT! EQU 2 ECHO -
IF !COUNT! EQU 3 ECHO /
IF !COUNT! EQU 4 ECHO -
IF !COUNT! EQU 4 (
SET COUNT=1
) ELSE (
SET /A COUNT+=1
)
PSLIST CALC >nul 2>&1
IF %ERRORLEVEL% EQU 1 GOTO END
GOTO BEGIN
:END
EDIT: This sample will start Calculator and then display a "spinner" until you close Calculator. I use pslist to check for the existence of CALC.EXE. The >nul 2>&1 redirects STDOUT and STDERR to nul so nothing from PSLIST will be displayed.
Try this:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CALL :BACKSPACE $BS
SET /A FULL_COUNT=60
SET /A MAX_COUNT=160
SET /A Spin_Delay=50
SET "_MSG=Process running..."
SET /A CTR=0
SET /A TCT=0
IF NOT [%1]==[] SET _MSG=%~1
IF NOT [%2]==[] SET /A FULL_COUNT=%2
IF NOT [%3]==[] SET /A SPIN_DELAY=%3
IF %FULL_COUNT% GTR %MAX_COUNT% SET FULL_COUNT=%MAX_COUNT%
(SET/P=%_MSG%*)<nul
FOR /L %%A IN (1,1,%FULL_COUNT%) DO (
CALL :DELAY %SPIN_DELAY%
IF !CTR! EQU 0 (set/p=%$BS%³)<nul
IF !CTR! EQU 1 (set/p=%$BS%/)<nul
IF !CTR! EQU 2 (set/p=%$BS%Ä)<nul
IF !CTR! EQU 3 (set/p=%$BS%\)<nul
SET /A CTR=%%A %% 4
)
(SET/P=%$BS%*)<nul
ENDLOCAL & EXIT /B %CTR%
:BackSpace
setlocal
for /f %%a in ('"prompt $H$S &echo on &for %%b in (1) do rem"') do set "Bs=%%a"
endlocal&call set %~1=%BS%&exit /b 0
:Delay msec
setlocal enableextensions
set/a correct=0
set/a msecs=%1+5
if /i %msecs% leq 20 set /a correct-=2
set time1=%time: =%
set/a tsecs=%1/1000 2>nul
set/a msecs=(%msecs% %% 1000)/10
for /f "tokens=1-4 delims=:." %%a in ("%time1%") do (
set hour1=%%a&set min1=%%b&set sec1=%%c&set "mil1=%%d"
)
if /i %hour1:~0,1% equ 0 if /i "%hour1:~1%" neq "" set hour1=%hour1:~1%
if /i %min1:~0,1% equ 0 set min1=%min1:~1%
if /i %sec1:~0,1% equ 0 set sec1=%sec1:~1%
if /i %mil1:~0,1% equ 0 set mil1=%mil1:~1%
set/a sec1+=(%hour1%*3600)+(%min1%*60)
set/a msecs+=%mil1%
set/a tsecs+=(%sec1%+%msecs%/100)
set/a msecs=%msecs% %% 100
:: check for midnight crossing
if /i %tsecs% geq 86400 set /a tsecs-=86400
set/a hour2=%tsecs% / 3600
set/a min2=(%tsecs%-(%hour2%*3600)) / 60
set/a sec2=(%tsecs%-(%hour2%*3600)) %% 60
set/a err=%msecs%
if /i %msecs% neq 0 set /a msecs+=%correct%
if /i 1%msecs% lss 20 set msecs=0%msecs%
if /i 1%min2% lss 20 set min2=0%min2%
if /i 1%sec2% lss 20 set sec2=0%sec2%
set time2=%hour2%:%min2%:%sec2%.%msecs%
:wait
set timen=%time: =%
if /i %timen% geq %time2% goto :end
goto :wait
:end
for /f "tokens=2 delims=." %%a in ("%timen%") do set num=%%a
if /i %num:~0,1% equ 0 set num=%num:~1%
set/a err=(%num%-%err%)*10
endlocal&exit /b %err%
If I understand your question you want a spinner because some operation you are performing is taking time and you want to show to the user that something is happening, right?
In that case, as far as I know, its not possible with the native commands. (it could be possible if you had a program that showed a spinner while executing the operation that take long time)
And it looks like the echo don't support ansi escape sequences (in the old days you had to have ansi.sys loaded, don't know if that still exists) so you can't use ansi to control the cursor.
The spinner CAN be done in batch script, you just need some variables:
#echo off
:spinner
set mSpinner=%mSpinner%.
if %mSpinner%'==..............................' set mSpinner=.
cls
echo %mSpinner%
rem Check if the process has finished via WMIC and/or tasklist.
goto spinner
:exit
For the BAT itself to detect a process running/exits. You can do that via the WMI command-line interface or the tasklist command of which I have limited knowledge.
If it were back in the DOS days you could even does that without clearing the screen... short of using some combination of escape characters. I don't know if it's still possible on Vista/XP.
If you mean within a Windows batch script, you can't do it natively. The echo statement used to print to the console will always print a newline, and you can't move the cursor.
It's a bit of a hack, but you can do this with a combination of VBScript and batch script.
This VBScript will print a backspace, then it's argument:
WScript.StdOut.Write(chr(8) & WScript.Arguments(0))
Put this in a file, vbsEcho.vbs, then call this script from your batch script. The following batch script will keep displaying the spinner until you press CTRL-C:
#echo off
:LOOP
cscript //nologo vbsEcho.vbs "\"
cscript //nologo vbsEcho.vbs "|"
cscript //nologo vbsEcho.vbs "/"
cscript //nologo vbsEcho.vbs "-"
goto :LOOP
EDIT: Using some of the ideas from aphoria's answer, this script will start the Windows calculator, and display a spinner until the calculator closes:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET COUNT=1
START CALC
cscript //nologo vbsEcho.vbs "Calculating: \"
:LOOP
IF !COUNT! EQU 1 cscript //nologo vbsEcho.vbs "|"
IF !COUNT! EQU 2 cscript //nologo vbsEcho.vbs "/"
IF !COUNT! EQU 3 cscript //nologo vbsEcho.vbs "-"
IF !COUNT! EQU 4 (
cscript //nologo vbsEcho.vbs "\"
set COUNT=1
) else (
set /a COUNT+=1
)
pslist CALC >nul 2>&1
if %ERRORLEVEL% EQU 1 goto :end
goto :LOOP
:END
cscript //nologo vbsEcho.vbs ". Done."
paxdiablos has an amazing answer, but having to echo your commands into a payload file is annoying. It's hard to read and hard to debug. I took his code and modified it a bit for my own use:
#echo off
:: Localise environment.
setlocal enableextensions enabledelayedexpansion
set wkdir=%~dp0%
set wkdir=%wkdir:~0,-1%
set done_flag="%wkdir%\tmp_payload.flg"
set timeout=7
:controller
IF (%1)==() (
call :monitor step1 "Getting stuff from SourceSafe: "
call :monitor step2 "Compiling some PHP stuff: "
call :monitor step3 "Finishing up the rest: "
) ELSE ( goto %1 )
goto final
:step1
::ping for 5 seconds
ping 127.0.0.1 -n 6 >nul
del "%wkdir%\tmp_payload.flg"
goto final
:step2
::ping for 10 seconds
ping 127.0.0.1 -n 11 >nul
del "%wkdir%\tmp_payload.flg"
goto final
:step3
::ping for 5 seconds
ping 127.0.0.1 -n 6 >nul
del "%wkdir%\tmp_payload.flg"
goto final
:monitor
:: Create flag file and start the payload minimized.
:: echo the word "dummy" to the flag file (second parameter)
echo >>%done_flag% dummy
:: start the command defined in the first parameter
start /min cmd.exe /c "test2.bat %1"
:: Start monitoring.
:: i is the indicator (0=|,1=/,2=-,3=\).
:: m is the number of seconds left before timeout.
set i=0
set m=%timeout%
set str=%2
for /f "useback tokens=*" %%a in ('%str%') do set str=%%~a
<nul (set /p z=%str%^|)
:: Loop here awaiting completion.
:loop
:: Wait one second.
ping 127.0.0.1 -n 2 >nul
:: Update counters and output progress indicator.
set /a "i = i + 1"
set /a "m = m - 1"
if %i% equ 4 set i=0
if %i% equ 0 <nul (set /p z=^|)
if %i% equ 1 <nul (set /p z=/)
if %i% equ 2 <nul (set /p z=-)
if %i% equ 3 <nul (set /p z=\)
:: End conditions, complete or timeout.
if not exist %done_flag% (
::echo.
echo Complete
goto :final
)
if %m% leq 0 (
echo.
echo. *** ERROR: Timed-out waiting for child.
goto :final
)
goto :loop
:final
endlocal
You can use a counter that prints a different character from a given set (like "\|/-") and you change the character according to like "counter modulo 4". Anyway, you don't say in which language you're working in so it is a bit difficult to be more precise.
EDIT: now that we know in which environment you're playing in, I'd say that the BAT/CMD language is not really up to the task... I'd recommend any scripting language, Ruby being my favorite.
I find the easiest way is to update the title - that way you don't have to do a CLS all the time.
The reason for the two lines of ping -n, is that it's quicker for ping to do a double ping of a second each, versus a single ping of two seconds.
Also, for those who don't know, a :: is the same as a REM, except that the comments are ignored at the beginning of the parser (I think this is the right word) instead of at the end. Simply put, that line is ignored.
:: begin spin.cmd
#echo off
setlocal
set COUNT=0
set MAXCOUNT=10
set SECONDS=1
:LOOP
title "\"
call :WAIT
title "|"
call :WAIT
title "/"
call :WAIT
title "-"
if /i "%COUNT%" equ "%MAXCOUNT%" goto :EXIT
set /a count+=1
echo %COUNT%
goto :LOOP
:WAIT
ping -n %SECONDS% 127.0.0.1 > nul
ping -n %SECONDS% 127.0.0.1 > nul
goto :EOF
:EXIT
title FIN!
endlocal
:: end spin.cmd
This routine examines the output of tasklist for a process you START from cmd.
Pass it the name of the exe as a parameter e.g.
call :spinner calc.exe
It reports
Elapsed: 001 seconds
and increments seconds until the exe process terminates.
The Elapsed 001 seconds message is overwritten each second by ECHO.exe -n \r
which echos a cr without a line feed.
Echo.exe is available at
http://www.paulsadowski.com/wsh/cmdprogs.htm
#echo off
start calc
call :spinner calc.exe
pause
:spinner
SET COUNT=1
:BEGIN
set "formattedValue=000000%count%"
ECHO.exe -n Elapsed: %formattedValue:~-3% seconds
ECHO.exe -n \r %= -n (suppress crlf) \r output a cr =%
SET /A COUNT+=1
set EXE=%1 %= search output of tasklist for EXE =%
set tl=tasklist /NH /FI "IMAGENAME eq %EXE%"
FOR /F %%x IN ('%tl%') DO IF %%x == %EXE% goto FOUND
set result=0
goto FIN
:FOUND
set result=1
:FIN
IF %result% EQU 0 GOTO END
PING -n 2 127.0.0.1 > nul %= wait for about 1 second =%
GOTO BEGIN
:END
start application, wait for loading
#echo off & setlocal enabledelayedexpansion
start application.exe
:1
for %%a in (^| ^/ ^- ^\ ^| ^/ ^- ^\) do (
for %%b in (^| ^/ ^- ^\ ^| ^/ ^- ^\) do (
for %%c in (^| ^/ ^- ^\ ^| ^/ ^- ^\) do (
cls &echo processing..%%c%%b%%a
sleep -m 20
IF EXIST "result file" (exit)
)))
goto 1
:LOOP
ECHOX -n "~r%Processing..."
IF %CTR% EQU 4 SET /A CTR=0
IF %CTR%==0 (set /p DOT=³)<NUL
IF %CTR%==1 (set /p DOT=/)<NUL
IF %CTR%==2 (set /p DOT=Ä)<NUL
IF %CTR%==3 (set /p DOT=\)<NUL
ECHOX -n "~r"
SET /A CTR+=1
SET /A TCT+=1
IF %TCT% GTR %MAX_COUNT% GOTO :END
GOTO :LOOP