Adding a Timeout to Set /p in batch - batch-file

I am trying to add a time out to Set /p Var1= So far I have only found this piece of code from here.
#echo off
setlocal EnableDelayedExpansion
if "%1" NEQ "" goto %1
del enter.tmp 2>nul >nul
start /b cmd /c %0 :secondThread
:FirstThread
set n=0
echo Enter Text (5 seconds timeout):
:loop
set /a n+=1
ping -n 2 localhost > nul
if !n! LSS 5 (
if not exist entER.tmp goto :loop
< Enter.tmp (
set /p input=
)
echo !input!
) ELSE (
echo Timeout for input
)
exit /b
:secondThread
set /p var=
> enter.tmp echo !var!
exit /b
This piece of code works great except it stops the count down only when the input is entered. I would like the count down to stop when any key is pressed. I don't know if this is possible. Thanks

Related

Batchfile: Problems to get errorlevel, it is always 0

scripters,
at first sorry for the last post.
Realy, i hope i could make you understand my problem now.
I realized that the problem is inside my script only.
If i run the line in the command prompt it works fine.
That means the Errorlevel is stored to exitCode.txt correctly.
START /B "some title" cmd /C "subroutine.bat& >"exitCode.txt" (call echo %^errorlevel%)& >"status.txt" (echo done) & (if defined return call echo %^return%) >"return.txt"" 1>"stdOut.txt" 2>"stdErr.txt"
This is a part of my script
So in :treath.run is the START /B .... line
Inside the script the errorlevel is not stored correctly inside the definded file.
Why?
The return Value is stored perfect.
I dont't get it.
Save this as main.bat
#echo off
setlocal ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
pushd "%~dp0"
:main
call :treath.import
call :treath.init "t1" "call subroutine.bat"
call :treath.run "t1"
:loop
call :treath.status "t1"
if "!return!"=="done" goto done
echo !return!
goto loop
:done
echo Now goto "!%t1%.stdExitCode!"
echo.
echo The Subroutine should return Errorlevel 1
echo But it is 0, in the file.
pause
EXIT
goto main
:::::::::::::::::::::::::::::::::::::::
:treath.import ()
set "return="
setlocal
set "objID=__OBJ_TREATH"
set "workdir=%tmp%\BATCH_OBJ\%objID%"
if not exist "%workdir%" (mkdir "%workdir%" || exit /b 1)
set /a "timeout=999"
:newSessionID
set /a "sessionID=%random%"
set "sessionDir=%workdir%\%sessionID%"
if exist "%sessionDir%" (
set /a "timeout-=1"
if !timeout! LSS 0 exit /b 1
goto newSessionID
)
mkdir "%sessionDir%" || exit /b 1
endlocal & (
rem set "%objID%_identifier=%objID%"
set "%objID%_version=0"
set "%objID%_sessionID=%sessionID%"
set "%objID%_sessionDir=%sessionDir%"
)
exit /b 0
:treath.init (treathName,"command")
set "return="
setlocal
rem hardcoded obj ident
set "objID=__OBJ_TREATH"
rem objID
set "prefix=%objID%_%~1"
rem treath folder
set "workdir=!%objID%_sessionDir!\%~1"
mkdir "%workdir%" || exit /b 1
set "command=%~2"
endlocal & (
set "%prefix%.command=%command%"
set "%prefix%.workdir=%workdir%"
set "%prefix%.stdOut=%workdir%\stdOut.txt"
set "%prefix%.stdErr=%workdir%\stdErr.txt"
set "%prefix%.stdDump=%workdir%\stdDump.txt"
set "%prefix%.stdExitCode=%workdir%\stdExitCode.txt"
set "%prefix%.return=%workdir%\return.txt"
set "%prefix%.stdStatus=%workdir%\stdStatus.txt"
set "%~1=%prefix%"
)
exit /b 0
:treath.run (treathName)
set "return="
setlocal
set "title=%~1"
set "prefix=!%~1!"
type NUL >"!%prefix%.stdExitCode!"
type NUL >"!%prefix%.stdOut!"
type NUL >"!%prefix%.stdErr!"
type NUL >"!%prefix%.stdStatus!"
rem errorlevel ist immer 0 ???
START /B "%title%" cmd /C "!%prefix%.command!& >"!%prefix%.stdExitCode!" (call echo %%^errorlevel%%)& >"!%prefix%.stdStatus!" (echo done) & (if defined return call echo %%^return%%) >"!%prefix%.return!"" 1>"!%prefix%.stdOut!" 2>"!%prefix%.stdErr!"
>"!%prefix%.stdStatus!" (echo running)
exit /b 0
:treath.status (treathName)
set "return="
setlocal
set "prefix=!%~1!"
endlocal & (
set /p "return="<"!%prefix%.stdStatus!" || exit /b 1
)
exit /b 0
Save this as "subroutine.bat"
set "return=This is a Test"
exit /b 1

Batch : Multiple for /f commands

I'm trying to do "game" for my friend but when i try to load from save file it will do half of it's job
Code
set penize=0
set penizesekunda=0
for /f "EOL=: tokens=* Delims=0" %%a in (penize.txt) do (
set penize=%%a
)
for /f "EOL=: tokens=* Delims=1" %%b in (penize.txt) do (
set penizesekunda=%%b
)
echo Penize %penize% penize za sekundu %penizesekunda%
sorry it in my language (penize = money, penizesekunda = money per second)
And input is 200 200 but it should be 500 200.
If you want full code :
#echo off
title Hra (Imui)
color 6a
echo Ahoj %computername% cas %time% datum %date%
echo.
echo.
echo Vitej ve hre
echo.
echo.
echo.
echo Tvym cilem bude ovladnout cely VESMIR
echo.
timeout>nul /t 3 /nobreak
echo Nacitani!
set penize=0
set penizesekunda=0
for /f "EOL=: tokens=* Delims=0" %%a in (penize.txt) do (
set penize=%%a
)
for /f "EOL=: tokens=* Delims=1" %%b in (penize.txt) do (
set penizesekunda=%%b
)
if errorlevel 1 do (
echo Pozor nebyla nalezena zadna ulozena hra nebo doslo k chybe startovani! (hry se nachazeji v souboru penize.txt)
timeout>nul /t 3 /nobreak
cls
goto Start
)
:Start
cls
set /p "Password=Zadej heslo > "
if %Password%== Admin goto RMV
if NOT %Password%== Imui goto FAIL
if %Password%== Imui goto HesloSpravne
goto Start
:Fail
cls
echo Spatne heslo!
echo Zadej heslo prosim (zbyvaji 2 pokusy)
set /p "Password=Zadej heslo > "
if %Password%== Admin goto RMVCzech
if NOT %Password%== Imui goto FailTwo
if %Password%== Imui goto HesloSpravne
goto Fail
:FailDva
cls
echo Spatne heslo!
color ac
echo Pozor! Mas uz jenom jeden pokus!
echo Prosim zadej heslo!
set /p "Password=Zadej heslo > "
if %Password%== Admin goto RMV
if NOT %Password%== Imui goto RMV
if %Password%== Imui goto HesloSpravne
goto FailTwo
:RMV
echo Smazavam ti tvoji hru
#ECHO ON
del "Hra.exe" /P /Q /F
echo Smazavani dokonceno!
Pause>nul
exit
:HesloSpravne
cls
echo Heslo bylo spravne
goto HlavniMenu
:HlavniMenu
echo Vitej
echo %penizesekunda% %penize%
Pause>nul
Content of penize.txt :
500
200
Another (more efficient) option is to use SET /P to read each line:
<penize.txt (
set "penize="
set /p "penize="
set "penizesekunda="
set /p "penizesekunda="
)
The simple SET statements that clear each variable are there just in case the line happens to be empty or missing in the file, in which case SET /P would preserve any pre-existing variable value.
The code can be shortened by using a FOR loop, especially if you have many more lines to read:
<penize.txt ( for %%V in (penize penizesekunda) do (
set "%%V="
set /p "%%V="
))
Note that you can list the variables on separate lines, which can help readability:
<penize.txt (
for %%V in (
penize
penizesekunda
) do (
set "%%V="
set /p "%%V="
)
)
Here is some example code for you:
REM Get first line of a file
set /p penize=<penize.txt
REM Get second line of a file
for /f "skip=1" %%a in (penize.txt) do set penizesekunda=%%a

Creating a Batch file that pings google constantly and testing the response time

I'm trying to create a batch file that will constantly ping google.com and check the response time - "time=Xms".
If the time <= 39ms the text of that ping(or the background) should
be green.
If the time > 40ms and < 80ms the text of that ping(or the
background) should turn orange.
If the time >= 80ms the text of that ping(or the background) should
turn red.
I have this batch at the moment which pings google every 3 seconds changes the background from green to red if the response fails:
#echo off
:color 97
:start
PING -n 1 www.google.com
call :color
goto :start
:color
IF %ERRORLEVEL% EQU 0 (
COLOR 27
) else (
COLOR 47
ping -n 1 127.0.0.1 >nul
COLOR 74
ping -n 1 127.0.0.1 >nul
COLOR 47
)
ping -n 3 127.0.0.1 >nul
GOTO:EOF
This works fine but I don't know how to test response times.
There are some quirks.
a) you have to get the desired value of ping to a variable. Use a for to get it.
b) you can't compare it directly, because if compares strings, not numbers (2 is bigger than 10). Add leading zeros to the string (and afterwards cut it to a fixed length)
c) cmd has no native way of coloring single lines (or characters). It can be done with pure cmd, but I think, powershell is a much better way to do it.
#echo off
:loop
set "tim=unreachable"
for /f "tokens=7 delims== " %%i in ('PING -n 1 www.google.com ^|find "TTL"') do set "tim=%%i"
set "ti=0000%tim%"
set "ti=%ti:~-6,-2%"
if %ti% leq 0040 powershell write-host -foreground green %tim% & goto :loop
if %ti% leq 0080 powershell write-host -foreground yellow %tim% & goto :loop
powershell write-host -foreground red %tim% & goto :loop
Try like this :
#echo off
Title Echo Ping Reply
mode con cols=57 lines=6
::Choose how many seconds you must wait for the refresh
Set MyPause=3
:color 97
:start
CLS
echo.
ping www.google.com | findstr /i "TTL"
Timeout /Nobreak /t %MyPause% > Nul
call :color
goto :start
:color
IF %ERRORLEVEL% EQU 0 (
COLOR 27
) else (
COLOR 47
ping -n 1 127.0.0.1 >nul
COLOR 74
ping -n 1 127.0.0.1 >nul
COLOR 47
)
ping -n 3 127.0.0.1 >nul
GOTO:EOF
Because sometimes PowerShell isn't an option, I figured I'd piece some things together and do this all as a batch script.
Writing a batch file to detect ping anomalies
How to have multiple colors in a Windows batch file?
:: usage: badpings-color.bat [ip adress | hostname]
#echo off
set /a warnlimit=79 :: greater than this value is red
:: between the two values is yellow
set /a goodlimit=39 :: less than or equal to this value is green
if "%1"=="" (
set pingdest=google.com
) else (
set pingdest=%1
)
echo Pinging %pingdest%.
::echo Logging replies over %limit%ms.
echo Press Ctrl+C to end.
:Loop
for /f "usebackq tokens=1-6" %%a in (`ping -n 1 %pingdest% ^| findstr "Request Reply request"`) do (
set var=%%a %%b %%c %%d %%e %%f
set pingtimestr=%%e
)
if "%pingtimestr%"=="find" (
echo Ping request could not find host %pingdest%. Please check the name and try again.
goto End
)
if "%pingtimestr%"=="host" (
set /a pingtime=%warnlimit%+1
)
if not defined pingtimestr (
set /a pingtime=%warnlimit%+1
)
if "%pingtimestr:~0,4%"=="time" (
set /a pingtime=%pingtimestr:~5,-2%
)
if %pingtime% LEQ %goodlimit% (
call :c 02 "[%time%] %var%" /n
goto EndOfLoop
)
if %pingtime% LEQ %warnlimit% (
call :c 0E "[%time%] %var%" /n
goto EndOfLoop
)
if %pingtime% GTR %warnlimit% (
call :c 0C "[%time%] %var%" /n
goto EndOfLoop
)
:EndOfLoop
timeout /t 1 /nobreak >nul
Goto Loop
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: this section is from
:: https://stackoverflow.com/questions/4339649/how-to-have-multiple-colors-in-a-windows-batch-file/10407642#10407642
:c
setlocal enableDelayedExpansion
:colorPrint Color Str [/n]
setlocal
set "s=%~2"
call :colorPrintVar %1 s %3
exit /b
:colorPrintVar Color StrVar [/n]
if not defined DEL call :initColorPrint
setlocal enableDelayedExpansion
pushd .
':
cd \
set "s=!%~2!"
:: The single blank line within the following IN() clause is critical - DO NOT REMOVE
for %%n in (^"^
^") do (
set "s=!s:\=%%~n\%%~n!"
set "s=!s:/=%%~n/%%~n!"
set "s=!s::=%%~n:%%~n!"
)
for /f delims^=^ eol^= %%s in ("!s!") do (
if "!" equ "" setlocal disableDelayedExpansion
if %%s==\ (
findstr /a:%~1 "." "\'" nul
<nul set /p "=%DEL%%DEL%%DEL%"
) else if %%s==/ (
findstr /a:%~1 "." "/.\'" nul
<nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%"
) else (
>colorPrint.txt (echo %%s\..\')
findstr /a:%~1 /f:colorPrint.txt "."
<nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%"
)
)
if /i "%~3"=="/n" echo(
popd
exit /b
:initColorPrint
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "DEL=%%A %%A"
<nul >"%temp%\'" set /p "=."
subst ': "%temp%" >nul
exit /b
:cleanupColorPrint
2>nul del "%temp%\'"
2>nul del "%temp%\colorPrint.txt"
>nul subst ': /d
exit /b
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:End

Only accept numeric characters in batch file input

I am making a game from a batch file and one of the inputs can accept any character (~!##$%^&*()`) and any other. Is there any way to look for any character other than numbers and use the GOTO command? This is my script so far:
set /p guess=
echo "%guess%"|findstr /L "[a-z][A-Z]~`!##$%^&*()-_=+\^|^^;:"',<.>/?*"
if %errorlevel% == 0 goto Invalid_Number
if %guess% == %number% goto Correct
... everything else here ...
:Invalid_Number
echo Invalid Number. Input must be a number
pause
Is there any way to make this work, all it says is Access Denied, I am testing this on a school computer though, it might not work.
Put this at the bottom of your script:
:isInt <str>
for /f "delims=0123456789" %%a in ("%1") do exit /b 1
exit /b 0
Then to invoke it, do
call :isInt %guess% && success || fail
Here's a more complete example:
#echo off
setlocal
set /a rand = %RANDOM% %% 10 + 1
:begin
set /P "guess=Guess a number between 1 and 10: "
call :isInt %guess% || goto invalid
if %guess% gtr 0 if %guess% lss 11 (
if %guess% equ %rand% (
echo Lucky guess!
exit /b
) else (
echo Oooh, so close. Try again.
goto begin
)
)
:invalid
echo Please enter a valid integer between 1 and 10.
goto begin
:isInt <str>
for /f "delims=0123456789" %%a in ("%1") do exit /b 1
exit /b 0
This is the same basic idea as MC ND's solution, but instead of using the for statement to unset %guess%, it sets %errorlevel% and stops looping at the first non-numeric character. This makes it infinitesimally more efficient. :)
And with either success or fail, I like to use conditional execution (the && and || stuff).
:ask
set /p "guess=?" || goto :ask
setlocal enabledelayedexpansion
for /f "delims=0123456789" %%a in ("!guess!") do set "guess="
endlocal & set "guess=%guess%"
if not defined guess (
echo invalid input
goto ask
)
echo valid input
The basic idea behind the test is to use the numbers as delimiters in a for /f command, so they are removed from the input. If anything remains it is not a number and the code in the do clause is executed.
The delayedexpansion is enabled/disabled to handle problematic characters (specially double quotes) that could be typed in the input field.
May I suggest you a different, better approach? Instead of read any line and then check if it contains a number, your program may directly read a number, so the checking is not necessary. The way to do that is emulating SET /P command via a subroutine. This way, you may add additional constraints to the input, like read a maximum number of digits, for example.
#echo off
rem Read a number emulating SET /P command
rem Antonio Perez Ayala
setlocal
rem Define the following variable before call InputNumber subroutine
set "thisFile=%~F0"
call :InputNumber number="Enter a number of up to 5 digits: " 5
echo Number read: %number%
goto :EOF
:InputNumber var="prompt" [digits]
setlocal EnableDelayedExpansion
rem Initialize variables
if "%~3" equ "" (set numDigits=9) else set "numDigits=%3"
set "digits=0123456789"
for /F %%a in ('copy /Z "%thisFile%" NUL') do set "CR=%%a"
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"
rem Show the prompt and start reading
set /P "=%~2" < NUL
set "input="
set i=0
:nextKey
set "key="
for /F "delims=" %%a in ('xcopy /W "%thisFile%" "%thisFile%" 2^>NUL') do if not defined key set "key=%%a"
rem If key is CR: terminate input
if "!key:~-1!" equ "!CR!" goto endRead
rem If key is BS: delete last char, if any
set "key=!key:~-1!"
if "!key!" equ "!BS!" (
if %i% gtr 0 (
set /P "=!BS! !BS!" < NUL
set "input=%input:~0,-1%"
set /A i-=1
)
goto nextKey
)
rem If key is not a digit: ignore it
if "!digits:%key%=!" equ "%digits%" goto nextKey
rem If can not accept more digits: ignore it
if %i% equ %numDigits% goto nextKey
rem Else: show and accept the digit
set /P "=%key%" < NUL
set "input=%input%%key%"
set /A i+=1
goto nextKey
:endRead
echo/
endlocal & set "%~1=%input%"
exit /B
You may also add any other processing to the input line, like show asterisks instead of digits, etc. For a large example on this topic, see this post

How to do math in batch-file

I have been having troubles with batch-codes that I would expect to work, but don't...
Below is what I have written...
#echo off
cls
:loop
set /p "input=Input a number: "
set /a "number=%input%" 2>nul
REM check if input valid
if "%input%" NEQ "%number%" (
cls
Echo Please Enter a valid number! &Echo.&Echo.
goto :loop
)
Set /a Even=number%%2
if %Even% EQU 0 (
Echo Substituting Even Number in: x / 2
Echo set /p"=(%number%) / 2 = "
set /a answer=number/2
) Else (
Echo Substituting Odd Number in: 3x - 1
<nul set /p"=3(%number%)-1 = "
set /a answer=number*3
set /a answer=answer-1
)
Echo %answer%
Echo.
Echo.
goto :loop
Echo Unexpected Error . . .
pause
Exit
Whenever I input a number into the console, it does the math, like I want it to, but prints the number -1, and every time i input another number, the number goes to -2, -3, -4, so on.
Put a setlocal enableextensions at the beginning after the #echo off, e.g.
#echo off
setlocal enableextensions
cls
Also, I think you would also need to use delayed variable expansion (usually denoted by !var!), which would change your script above to something like this:
#echo off
setlocal enableextensions enabledelayedexpansion
cls
:loop
set /p "input=Input a number: "
set /a number=!input! 2>nul
REM check if input valid
if "!input!" NEQ "!number!" (
cls
Echo Please Enter a valid number!
Echo.
Echo.
goto :loop
)
REM Make sure that it is an integer put in (just in case)
set /a int=!number! %% 1
if "!input!" NEQ "!int!" (
cls
Echo Please Enter a valid number!
Echo.
Echo.
goto :loop
)
Set /a Even=!number! %% 2
if !Even! EQU 0 (
Echo Substituting Even Number in: x / 2
set /a answer=!number! / 2
) Else (
Echo Substituting Odd Number in: 3x - 1
set /a answer=!number! * 3 - 1
)
Echo !answer!
Echo.
Echo.
goto :loop
I also would like to point out that I also fixed a few other bugs (set /p isn't of any use in this script at all, especially in where it is used, and also you need the modulus to find even/odd).

Resources