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 by findstr. 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