Page 1 of 2

Only accept a numeric number.

Posted: 30 Jul 2020 09:54
by PAB
Good afternoon!

I want to be able to use the user input value as a variable further down some code rather than having to hard code the number.
The code below works, but it relies on the fact that there actually is a number input and it is a number that is valid. Here is the test code I have got . . .

Code: Select all

@echo off
setlocal enabledelayedexpansion

:Menu

set "userinput="
set /p "userinput=Write something . . ."

if "%userinput%"==""    echo. & echo  Invalid & echo. & echo ^>Press ANY Key to try again . . . & pause >nul & goto :Menu
if "%userinput%" gtr 12 echo. & echo  Invalid & echo. & echo ^>Press ANY Key to try again . . . & pause >nul & goto :Menu

echo. & echo %userinput%

pause & goto :Menu
The code above doesn't accept the user just hitting <ENTER> and returns them to the :Menu, that's OK.
The code above doesn't accept the user inputting a number greater than 12 and returns them to the :Menu, that's OK.

What I can't seem to be able to do is to make sure that ONLY a number is input. If I input a letter, it accepts it. If a letter is entered I want it to give the same message and goto :Menu. I ONLY want it to accept numbers 1,2,3,4,5,6,7,8,9,10,11,12.

I have scoured the Internet for a long time but been unable to come up with anything close to what I want to achieve.

I found this bit of code [ which I have tried to adapt ] which looked promosing but I can't get it ignore letters and ONLY accept numbers!

Code: Select all

echo %userinput%|findstr /r /c:"^[0-9][0-9]*$" >nul
if errorlevel 1 (
  echo. & echo  Invalid & echo. & echo ^>Press ANY Key to try again . . . & pause >nul & goto :Menu
) else (
set "OS_Index=%userinput%
)
Any help will be greatly appreciated.

Thanks in advance.

Re: Only accept a numeric number.

Posted: 30 Jul 2020 13:52
by T3RRY

Code: Select all

@Echo Off
:Input <VarName>
  Setlocal EnableDelayedExpansion
:Fetch
  Set /P "input=Enter %1: "
  Echo/!input!|"%__AppDir__%findstr.exe" /RX "[0-9]*" >NUL||Goto :Fetch
If "!input!"=="0" (Goto :Fetch) Else If !Input! GTR 12 Goto :Fetch
(
  Endlocal & Set "%1=%input%"
  Exit /B
)

Re: Only accept a numeric number.

Posted: 30 Jul 2020 14:07
by penpen
You could also use a for/f-loop:

Code: Select all

for /f "delims=0123456789" %%a in ("!userinput!") do goto :Menue
penpen

Re: Only accept a numeric number.

Posted: 30 Jul 2020 16:31
by PAB
Thank you BOTH VERY MUCH for the replies, it is appreciated.

I have two problems . . .

[1] I can' get the greater than 12 to work properly.

[2] I can't seem to add an OS_Index variable that will hold the value of %userinput%. The reason for this is that I use %userinput% further down the code so setting the value of %userinput% in %OS_Index% means that the value will NOT get overwritten when I use %userinput% further down. I hope this makes sense!

Code: Select all

@echo off
setlocal enabledelayedexpansion

:Menu

echo.

set "userinput="
set /p "userinput=Input Index Number: "

if "%userinput%"==""    goto :Menu
if "%userinput%" gtr 12 goto :Menu
for /f "delims=0123456789" %%a in ("%userinput%") do goto :Menu

echo. & echo %userinput%

goto :menu
Thanks in advance.

Re: Only accept a numeric number.

Posted: 30 Jul 2020 16:35
by Squashman
You are comparing a string to an integer because you have the quotes. The quotes are included in the comparison.

Code: Select all

if "%userinput%" gtr 12 goto :Menu
Just remove the quotes to compare the values as integers.

Code: Select all

if %userinput% gtr 12 goto :Menu
Looking back at some of your previous code you did the comparison without quotes when comparing an integer. So not sure why you chose to use quotes this time.

Re: Only accept a numeric number.

Posted: 30 Jul 2020 16:59
by PAB
Thanks Squashman, that works great. I will keep trying to sort out number [2] above if I can!

Yes, I have always had a problem with quotes knowing when and when not to use them, but I am gradually getting to understand it a bit more. They say you can't teach an old dog new tricks, but I do try, really.

Thanks again.

EDIT:

I just looked back at my code in the script I am writing and I have used . . .

Code: Select all

if "%userinput%" gtr "2"
. . .and it works there, very strange!

I know why it works there, it is because I only input 0 or 1.

Re: Only accept a numeric number.

Posted: 30 Jul 2020 18:22
by Squashman
PAB wrote:
30 Jul 2020 16:59
I just looked back at my code in the script I am writing and I have used . . .

Code: Select all

if "%userinput%" gtr "2"
. . .and it works there, very strange!

I know why it works there, it is because I only input 0 or 1.
Yes that is safe with single integers.

Code: Select all

H:\>if "12" gtr "2" echo yes

H:\>if "2" gtr "12" echo yes
yes

Re: Only accept a numeric number.

Posted: 31 Jul 2020 01:55
by PAB
Good morning.

Thanks for the help EVERYONE, it is appreciated.
Here is the finished code.
It will only accept numbers 1 to 12 which is what I want.
It will hold the value of %OS_Index% so I can use it in several sections of the code further down.

Code: Select all

@echo off
setlocal enabledelayedexpansion

:Menu

set "OS_Index="
set /p "OS_Index=Input Index Number: "

if "%OS_Index%"==""   goto :Menu
if  %OS_Index% gtr 12 goto :Menu
if  %OS_Index% lss  1 goto :Menu
for /f "delims=0123456789" %%a in ("%OS_Index%") do goto :Menu
Thanks again.

Re: Only accept a numeric number.

Posted: 31 Jul 2020 02:50
by Aacini
IMHO, "only accept a number between 1 and 12" is something entirely different to "accept any string and check if it is a number between 1 and 12. If not, repeat the input". The code below do the first thing:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem Initialize variables
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"

set /P "=Enter the month: " < NUL
call :ReadMonth
echo Month read: "%month%"
goto :EOF


:ReadMonth

set "month="
set i=0

:nextKey
   set "key="
   for /F "delims=" %%a in ('xcopy /W "%~F0" "%~F0" 2^>NUL') do if not defined key set "key=%%a"

   rem If key is CR: terminate input, if any. Else ignore key
   if "!key:~-1!" equ "!CR!" (
      if %i% gtr 0 (goto endRead) else goto nextKey
   )

   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 "month=%month:~0,-1%"
         set /A i-=1
      )
      goto nextKey
   )

   rem Insert here any filter on the key
   if %i% equ 0 (
      if %key% lss 1 goto nextKey
      if %key% gtr 9 goto nextKey
   ) else if %i% equ 1 (
      if "%month%" neq "1" goto nextKey
      if %key% lss 0 goto nextKey
      if %key% gtr 2 goto nextKey
   ) else (
      goto nextKey
   )

   rem Else: show and accept the key
   set /P "=%key%" < NUL
   set "month=%month%%key%"
   set /A i+=1

goto nextKey

:endRead
echo/
exit /B
Antonio

Re: Only accept a numeric number.

Posted: 31 Jul 2020 10:36
by penpen
You should change the order and use delayed expansion when testing for other characters, else someone could inject commands:

Code: Select all

for /f "delims=0123456789" %%a in ("!OS_Index!") do goto :Menu
if "%OS_Index%"==""   goto :Menu
if  %OS_Index% gtr 12 goto :Menu
if  %OS_Index% lss  1 goto :Menu
penpen

Re: Only accept a numeric number.

Posted: 31 Jul 2020 12:24
by Compo
I would probably do it like this, (Obviously the last line would be replaced with your code):

Code: Select all

@Echo Off

:Menu
Set "OS_Index="
Set /P "OS_Index=Input Index Number [1..12]>"

If Not Defined OS_Index (GoTo Menu)Else (
	Set OS_Index | "%__AppDir__%findstr.exe" ^
	"^OS_Index=[123456789]$ ^OS_Index=[1][012]$" 1> NUL ^
	|| GoTo Menu)

Echo passed & Pause
This should work regardless of what the end user inputs, (as long as it isn't a CTRL key sequence).

Re: Only accept a numeric number.

Posted: 03 Aug 2020 12:56
by PAB
First of all, thank you to everyone who has contributed to this question, it is very much appreciated!
penpen wrote:
31 Jul 2020 10:36
You should change the order and use delayed expansion when testing for other characters, else someone could inject commands:

Code: Select all

for /f "delims=0123456789" %%a in ("!OS_Index!") do goto :Menu
if "%OS_Index%"==""   goto :Menu
if  %OS_Index% gtr 12 goto :Menu
if  %OS_Index% lss  1 goto :Menu
penpen
I had to change that code because it didn't work and just exited the program.
So I changed the % to ! and it works fine . . .

Code: Select all

@echo off
setlocal enabledelayedexpansion

:Menu

set "OS_Index="
set /p "OS_Index=Enter the INDEX number [1...12] and press <Enter>: "

for /f "delims=0123456789" %%a in ("!OS_Index!") do goto :Menu
if !OS_Index!==""    goto :Menu
if !OS_Index! gtr 12 goto :Menu
if !OS_Index! lss  1 goto :Menu
I then just had to use !OS_Index! in the code further down.

Thanks again everyone.

Re: Only accept a numeric number.

Posted: 05 Aug 2020 18:27
by penpen
PAB wrote:
03 Aug 2020 12:56
I had to change that code because it didn't work and just exited the program.
So I changed the % to ! and it works fine . . .
You are wrong on that; the code is working without any issues.

I suspect you removed the doublequotes around %OS_Index%, which would cause the behaviour you described, because after changing % to ! in your code you ended up with the following code for that line:
PAB wrote:
03 Aug 2020 12:56

Code: Select all

if !OS_Index!==""    goto :Menu
Also note that your detection of empty inputs actually bases on the last comparison (lss 1) and the fact that the exclamation-mark-character is lss than the 1-character. So in case you still want to use delayed expansion for those lines, you could leave out the above explicit test for empty input-words.
But i wouldn't recomment you to rely on that fact, because there is no guarantee, that this behaviour is working for other versions of windows or with the next win10-patch.


penpen

Re: Only accept a numeric number.

Posted: 06 Aug 2020 12:34
by Compo
PAB wrote:
03 Aug 2020 12:56
First of all, thank you to everyone who has contributed to this question, it is very much appreciated!
penpen wrote:
31 Jul 2020 10:36
redacted
I had to change that code because it didn't work and just exited the program.
redacted
Why didn't you try my single line example, (which was split over multiple lines for ease of reading), instead of using those four lines?

Code: Select all

If Not Defined OS_Index (GoTo Menu)Else Set OS_Index|"%__AppDir__%findstr.exe" "^OS_Index=[123456789]$ ^OS_Index=1[012]$">NUL||GoTo Menu

Re: Only accept a numeric number.

Posted: 07 Aug 2020 13:31
by PAB
Hello penpen,
penpen wrote:
05 Aug 2020 18:27
PAB wrote:
03 Aug 2020 12:56
I had to change that code because it didn't work and just exited the program.
So I changed the % to ! and it works fine . . .
You are wrong on that; the code is working without any issues.

I suspect you removed the doublequotes around %OS_Index%, which would cause the behaviour you described, because after changing % to ! in your code you ended up with the following code for that line:
PAB wrote:
03 Aug 2020 12:56

Code: Select all

if !OS_Index!==""    goto :Menu
Also note that your detection of empty inputs actually bases on the last comparison (lss 1) and the fact that the exclamation-mark-character is lss than the 1-character. So in case you still want to use delayed expansion for those lines, you could leave out the above explicit test for empty input-words.
But i wouldn't recomment you to rely on that fact, because there is no guarantee, that this behaviour is working for other versions of windows or with the next win10-patch.
Not being a programmer but just an old guy trying to learn I do find that sometimes it is a bit beyond me.
So let me see if this is correct . . .

Code: Select all

  set "OS_Index="

  set /p "OS_Index=>Enter the INDEX number and press <Enter>: "
  for /f "delims=0123456789" %%a in ("!OS_Index!") do (goto :Menu)
  if  "%OS_Index%"==""   goto :Menu
  if   %OS_Index% gtr 12 goto :Menu
  if   %OS_Index% lss  1 goto :Menu
  echo. & echo  You have selected INDEX number %OS_Index%. 
  echo. & echo ^>Press ANY key to continue . . . & pause >nul
  goto :wim_Menu
Then to use the number that is now stored in %OS_Index% I would just use %OS_Index% in a cmd further down the code, or would I need to use "%OS_Index%"
Is that correct please?
I just thought that once you started using !, you should carry on using them in a For loop and I didn't realise that they can both be used together, or am I wrong?

Thanks in advance.