Check for only numerical input in batch file
Solution 1:
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.
Solution 2:
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
Solution 3:
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.
Solution 4:
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