In the Windows NT-based command line (mostly for XP or higher), is there a way to verify if a switch being provided is a number only? Depending on the number, I want it to loop through the code x number of times
19 Answers
Edited to fix the regex as per debham's comment. Turns out that adding a space before the pipe after echo adds a space to the piped string, which was what broke the start/end of line matching previously. The regex could be further improved by discarding whitespace at the beginning and end.
There's the findstr command. It can search files with regular expressions, much like grep in Linux. It can also search piped input.
@echo off set param=%1 echo %param%| findstr /r "^[1-9][0-9]*$">nul if %errorlevel% equ 0 ( echo Valid number ) Explanation
The parameter used is set into the param variable. However, there is nothing stopping you using the parameter directly (%1 for the first paramater).
findstr is used to search the piped input with the /r flag for regex.
The pattern:
^means beginning of line.[0-9]means a digit. The*means the previous repeated zero or more times. So[0-9][0-9]*means one digit, plus zero or more digits. In other words, at least one digit. The+one or more times does not seem to be supported byfindstr. Note that[1-9]is used for the first digit to disallow leading zeroes - see the comments.$means end of line.
Now, a for loop in batch for x number of times... if x is not a valid number, the loop actually does not run at all - it just gets skipped for the next line. So there is no need to check if the input is a valid number!
@echo off set param=%1 for /l %%a in (1,1,%param%) do ( echo %%a ) The loop is done using for /l, where (x,y,z) means start at x, increment by y until z is reached. And it sets %%a to the current number/iteration.
Note: this actually fails if there is a leading zero, causing the command processor to treat it as an octal number. See dbenham's answer for a better solution.
7The following works very well for me. SET /a param=%1+0 always returns 0 if %1 is empty or not numeric. Otherwise, it provides the given number.
SET /a param=%1+0 IF NOT %param%==0 ECHO Valid number 1This will detect if the first parameter is a valid natural number (non-negative integer).
@echo off echo %1|findstr /xr "[1-9][0-9]* 0" >nul && ( echo %1 is a valid number ) || ( echo %1 is NOT a valid number ) If you want to allow quotes around the number, then
@echo off echo "%~1"|findstr /xr /c:\"[1-9][0-9]*\" /c:\"0\" >nul && ( echo %~1 is a valid number ) || ( echo %~1 is NOT a valid number ) Note - leading zeros are disallowed because batch treats them as octal, so a value like 09 is invalid, and 010 has a value of 8.
Inspired by Bob's excellent answer with the following additions
- Fix the errorlevel logic
- Implement a DoWork loop/sub that iterates through the number and does some arithmetic
::::::::::::::: ::CountTo.bat ::::::::::::::: @echo off ::This is the number to test if "%1"=="" goto :Usage set param=%1 set matchPattern="^[1-9][0-9]*$" ::test if param matches matchPattern, quietly :: (redirect stdout to nul, and stderr to stdout) echo %param%|findstr /r %matchPattern%>nul 2>&1 :: check for errorlevel 1 or higher. errorlevel 0 is handled as :: an unchecked fall-through if errorlevel 1 goto :MyHandleErrorCode ::Success (errorlevel ! >= 1) so proceed. echo %param% is a valid number echo (matches findstr /r %param% %matchPattern%) echo findstr returned errorlevel 0 ::any other code that the batch file needs to do goes here echo. echo Iterating from 1 to %param% echo. for /l %%i in (1,1,%param%) do call :DoWork %%i ::anything else, :: . :: . ::cleanup :: . :: . ::exit the batch file here, skipping embedded subroutines goto :eof ::::::::::::::::::::::::: :: Main work subroutine ::::::::::::::::::::::::: :DoWork set /a offset = %1 - 1 set /a square = %1 * %1 echo item %1 echo offset: %offset% echo square: %square% echo. goto :eof ::::::::::::::::::::::: :: Error handler code ::::::::::::::::::::::: :MyHandleErrorCode echo. echo CountTo %param% echo %param% is not a valid number echo (does not match findstr /r %param% %matchPattern%) echo findstr returned errorlevel ^>= 1 :: error code doesn't have a goto :eof, we want to drop through to :Usage :::::::: :Usage :::::::: echo. echo Usage: echo. CountTo ^<someNumber^> The problem with the following is that it always returns 'Valid number' because 'if errorlevel 0' is always true due to the fact that it is a '>= 0' compare, not a '== 0' compare.
@echo off set param=%1 echo %param%| findstr /r "^[1-9][0-9]*$">nul if errorlevel 0 ( echo Valid number ) my $.02
1You can validate any variable if its number:
SET "var="&for /f "delims=0123456789" %i in ("%a") do set var=%i if defined var (echo."NIC">nul) else (echo."number") set xx=33 echo %xx% SET /a %xx%+0 2>nul >nul && echo all Digits set xx=3x3 echo %xx% SET /a %xx%+0 2>nul >nul || echo No Digits 1I don't know why but on my system the findstr command didn't work. The errorlevel wasn't being changed on match or no match.
I did come up with another method though
:: Look for all digits. Parse the string and use all the digits as :: delimiters. If the entire string is a digit then we will get an empty :: parse value. SET ALL_DIGITS=0 FOR /F "tokens=* delims=0123456789" %%a IN ("%VALUE%") DO ( IF "[%%a]" EQU "[]" SET ALL_DIGITS=1 ) IF %ALL_DIGITS% EQU 0 ( ECHO ERROR: %VALUE% is not all numbers ) One approach:
set test_var=001 ( (if errorlevel %test_var% break ) 2>nul )&&( echo %test_var% is numeric )||( echo %test_var% is NOT numeric ) set test_var=001X ( (if errorlevel %test_var% break ) 2>nul )&&( echo %test_var% is numeric )||( echo %test_var% is NOT numeric ) Another:
@echo off :isInterer input [returnVar] setlocal enableDelayedexpansion set "input=%~1" if "!input:~0,1!" equ "-" ( set "input=!input:~1!" ) else ( if "!input:~0,1!" equ "+" set "input=!input:~1!" ) for %%# in (1 2 3 4 5 6 7 8 9 0) do ( if not "!input!" == "" ( set "input=!input:%%#=!" ) ) if "!input!" equ "" ( set result=true ) else ( set result=false ) endlocal & if "%~2" neq "" (set %~2=%result%) else echo %result% :main set /p input=text if %input% equ 0 goto valid set /a inputval="%input%"*1 if %inputval% equ 0 goto invalid goto valid :invalid echo Input is not an integer. :valid echo Input is an integer. Here is a game that uses this function.
@echo off :main set /a guessmin=1 set /a guessmax=100 set /a guessrand=%random% %%100 +1 echo. :check if %guessmin% equ %guessmax% goto fail set /p input=- Pick a number %guessmin% - %guessmax%: set /a inputval="%input%"*1 if %inputval% equ 0 goto invalid if %inputval% gtr %guessmax% goto invalid if %inputval% lss %guessmin% goto invalid if %inputval% gtr %guessrand% goto high if %inputval% lss %guessrand% goto low if %inputval% equ %guessrand% goto mid :invalid echo Please enter a valid number. echo. goto check :high echo Your guess was too high. echo. set /a guessmax=%inputval%-1 goto check :low echo Your guess was too low. echo. set /a guessmin=%inputval%+1 goto check :mid echo Your guess was correct. The game will now reset. set /p input=- Press enter to play again. cls goto main :fail echo You actually managed to lose because there is only echo one number remaining. That number is %guessrand%. set /p input=- Press enter to lose again. cls goto main