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
9 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.
-
-
1As written, this solution will fail with a parameter like
"this 1 fails"
The FINDSTR should do an exact match. It should not do a word match.– dbenhamJul 11, 2013 at 11:56 -
@dbenham Thanks. I tried beginning/end of line matching before, but that failed due to echo passing an extra space. Fixed now.– BobJul 11, 2013 at 14:19
-
-
The 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
-
4This will fail if parameter = 0. Also will treat a parameter like "1+1" as a number, which might cause problems, depending on requirements.– dbenhamJul 11, 2013 at 11:59
This 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
-
Whoops... fixed the errorlevel on mine too. Direct numerical comparison rather than the automatic fall-through.– BobOct 23, 2016 at 0:35
You 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
-
2
I 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
set /a NO_LINES=%~1
; 2.if %NO_LINES% NEQ %~1 goto ABORT
. When the two are equal - The variable or parameter is-numeric.