-1

Iv been tinkering with a batch program, but the menus crash when wrong answers are input.

iv figured out how to prevent a crash when a single space is entered but that is just a slight improvement.

IF "%INPUT%"==" " GOTO :menu ::Prevents single space input crash

I've been using:

SET INPUT=
SET /P INPUT=
IF /I '%INPUT%'=='95' GOTO  Credits
IF /I '%INPUT%'=='96' GOTO  Help

I've been using SET INPUT= over Choice command because the choice command limits the input to 9 numerical variables, and i just don't want to use the alphanumerical values A-Z. I have tried to use input errors that redirect to a menu If errorlevel = 1 goto :menu.

But this effects every input for some reason and won't allow proper inputs to function.

Compo
  • 36,585
  • 5
  • 27
  • 39
  • 2
    `If errorlevel == 1` or `if %errorlevel%=1` (and note lack of spaces). See `If /?` you are mixing `if` syntax. You don't mention what is a incorrect answer. –  Dec 26 '19 at 00:57
  • 2
    Wherever possible use `choice.exe` for your menu of options. – Compo Dec 26 '19 at 01:32
  • 2
    A good choice to avoid problems with spaces is to surround with quotes, like `if "%errorlevel%" = "1"` – dcg Dec 26 '19 at 02:28
  • 2
    @Mark It would be indeed a good idea to run in a [command prompt](https://www.howtogeek.com/235101/) `if /?` and read the output help. `if errorlevel == 1` does not work as this condition comparing the string `errorlevel` with the string `1` evaluates always to false. The two strings are never equal. `if %errorlevel%=1` contains multiple syntax errors. The equal string comparison operator is `==` and not `=`. `=` is an invalid operator. The spaces around the operator are missing additionally to get valid syntax `if string1 == string2` although `cmd.exe` detects and corrects this syntax error. – Mofi Dec 26 '19 at 09:47
  • 1
    @dcg Double quotes around a string to compare is usually a good idea, but not necessary on evaluating the __integer__ value of environment variable `errorlevel`. This environment variable holds always an integer value consisting of only digits and very unlikely but possible a `-` at beginning in case of a negative integer (not recommended as exit code). Also the operator is `==` and not `=`. The recommended syntax for evaluating exit code of an application is described on first help page output on running in a [command prompt](https://www.howtogeek.com/235101/) `if /?`. – Mofi Dec 26 '19 at 09:51
  • 1
    I recommend to read [How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?](https://stackoverflow.com/a/49834019/3074564) and [Safe number comparison in Windows batch file](https://stackoverflow.com/a/57111885/3074564). – Mofi Dec 26 '19 at 10:03
  • @Mofi Thank you. What I tried to say is that, in general, is good practice surrounding with quotes (at least for me). The `=` was a typo, sorry for that. – dcg Dec 26 '19 at 15:26

2 Answers2

1

So... You want VALID input to consist only of numbers, correct?

The following will only accept a response between a valid min and max number (note you can't have min and max clause beyond +/- 147 Million. with this code and 0 and -1 are not valid options so you might prefer to just use a set of p[ositive integers in the range 1 to + )

This code will validate that a value between your min and max was entered, it will allow leading 0s to be entered, and ignore them.

Any other character used will result in a failure, and print the menu and ask for the choice again.

From -147,483,647 to +147,483,647 (excluding 0 and -1)

@( setlocal Enabledelayedexpansion
  Echo off
  Set /A "Min_Num=-147483647", "Max_Num=147483647", "_CONSTANT_INVALID_CHOICE_A=0", "_CONSTANT_INVALID_CHOICE_B=-1"
)
CALL :Main

( endlocal
  Exit /B
)


:Main
  :1_Main
  CALL :Menu
  Choice /M "Test Choice Again?"
  GOTO :%ERRORLEVEL%_Main
  :2_Main
GOTO :EOF

:Menu
  CLS
  SET "_Input="
  SET "tNum="
  ECHO.
  ECHO.Output Menu Options here.
  ECHO.
  Set /p "_Input=Please enter a number between %Min_Num% and %Max_Num%: "
  SET /A "tNum= (1%_Input%) - ( (2%_Input%) - (1%_input%) )" || (
    GOTO :Menu  )
  FOR %%A IN ( %_CONSTANT_INVALID_CHOICE_A% %_CONSTANT_INVALID_CHOICE_B%
  ) DO (
    IF %tNum% EQU %%A  GOTO :Menu  )
  IF %tNum% LEQ %Max_Num% (
    ECHO=%tNum% LEQ %Max_Num%
    IF %tNum% GEQ %Min_Num% (
      ECHO.VALID CHOICE %tNum%
      CALL :Choice_%tNum%
      GOTO :EOF
    )
  )
  GOTO :Menu
GOTO :EOF

:Choice_-2
  ECHO=Choice -2
GOTO :EOF
:Choice_1
  ECHO=Choice 1
GOTO :EOF
:Choice_2
  ECHO=Choice 1
GOTO :EOF
  ECHO=Choice 1
:Choice_3
  ECHO=Choice 1
GOTO :EOF

From 1 to +147,483,647

@( setlocal Enabledelayedexpansion
  Echo off
  Set /A "Min_Num=1", "Max_Num=147483647",
)
CALL :Main

( endlocal
  Exit /B
)


:Main
  :1_Main
  CALL :Menu
  Choice /M "Test Choice Again?"
  GOTO :%ERRORLEVEL%_Main
  :2_Main
GOTO :EOF

:Menu
  CLS
  SET "_Input="
  SET "tNum="
  ECHO.
  ECHO.Output Menu Options here.
  ECHO.
  Set /p "_Input=Please enter a number between %Min_Num% and %Max_Num%: "
  SET /A "tNum= (1%_Input%) - ( (2%_Input%) - (1%_input%) )" || (
    GOTO :Menu  )
  IF %tNum% LEQ %Max_Num% (
    ECHO=%tNum% LEQ %Max_Num%
    IF %tNum% GEQ %Min_Num% (
      ECHO.VALID CHOICE %tNum%
      CALL :Choice_%tNum%
      GOTO :EOF
    )
  )
  GOTO :Menu
GOTO :EOF

:Choice_1
  ECHO=Choice 1
GOTO :EOF
:Choice_2
  ECHO=Choice 1
GOTO :EOF
  ECHO=Choice 1
:Choice_3
  ECHO=Choice 1
GOTO :EOF
Ben Personick
  • 3,074
  • 1
  • 22
  • 29
0

An all purpose input function for strings requiring validation to a given pattern or character set that uses Arg 1 as the variable to be defined [as well as the input prompt] with findstr regular expressions as Arg 2 to whitelist any input matching the supplied pattern.

Help file with usage included. Can be used as a function within a batch script or as a standalone .bat file

::GetIN [ReturnVar] ["qouted findstr search pattern"]  [Optional:"/C" - required for patterns with spaces]
@Echo off
 SETLOCAL EnableExtensions EnableDelayedExpansion
rem - lines prefixed with ::: form part of this scripts Help Documentation. Do not remove.
> "%TEMP%\%~n0_help.~tmp" (For /F "Delims=" %%G in ('Type "%~f0"^| Findstr.exe /BLIC:":::" 2^> nul ')Do For /F "Tokens=1* Delims=:" %%v in ("%%G")Do Echo/^|%%v)
:::================================================================================================================
:::     [ GetIn Input function or standalone script for validating input with findstr regular expressions. ]
:::                     Version = 2.2        Release = October 2020       Author = T3RRY 
:::========================================= ^^^! important notes on usage ^^^! =========================================
:::          ^^^! Pipes not supported ^^^!
:::    ^     + Not suited for use with pipes due to forced loop for failed matches
:::  < ^^^! >   + Test your regex pattern to ensure it is valid for the required input
:::    v       format to prevent your user/s being stuck in a goto loop.
:::            Error output for invalid search patterns is not displayed.
:::          + EXIT.CODES - Errorlevel assessment after call:
:::            - Errorlevel 3 - User has failed to provide input * OR * regex pattern is
:::              invalid * OR * GetIn is a called label in a file that has recieved input
:::              from a pipe that has not been handeled before the call.
:::            - Errorlevel 2 - GetIn is a script that has been invoked by a pipe
:::            - Errorlevel 1 - GetIn help has been Invoked * OR * GetIn has been called
:::              without Arg 2 for Regex Pattern
:::            - Errorlevel 0 - Input has been assigned that matches the regex Pattern
:::================================================================================================================
rem [* Display GetIn Usage with findstr regex info and practical examples *]
>> "%TEMP%\%~n0_help.~tmp" (
  Echo/^| USAGE: Call %~0 [ReturnVar] ["findstr search pattern"] [Optional:"/C" -required for patterns with spaces]
  Echo/^|================================================================================================================
  Echo/^| Examples [ Description is replaced with Arg 1-ReturnVar ]:
  Echo/^|
  Echo/^|    Call %~0 4-Digit-Number "[0-9][0-9][0-9][0-9]"
  Echo/^|    Call %~0 Alphanumeric-String "[0-9a-zA-Z]*"
  Echo/^|    Call %~0 semicolon-delimited-hexadecimal-String "[0-9a-fA-F][0-9a-fA-F][:][0-9a-fA-F][0-9a-fA-F]"
  Echo/^|    Call %~0 AlphaString-with-3-Digit-Numeric-suffix "[a-zA-Z]*[0-9][0-9][0-9]"
  Echo/^|    Call %~0 list-of-delimited-words "[a-zA-Z, ]*" /C
  Echo/^|    Call %~0 pair-of-delimited-numbers "[0-9]*[, ][0-9]*" /C
  Echo/^|
  Echo/^|    ^^     Limit of 15 character class expressions applies
  Echo/^|  ^< ^^^! ^>   Case sensitive classes should use actual letters as [STRING] instead of range [A-Z]
  Echo/^|    v     See: https://stackoverflow.com/a/20159191/12343998
  Echo/^|                                                     -Note: %~0 does not support Input from pipes.
  Echo/^|----------------------------------------------------------------------------------------------------------------
  For /F "Delims=" %%G in (' Findstr.exe /? ^| More +33 ') Do Echo/^|  %%G
  Echo/^|  https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/findstr
  Echo/^|  recommended reference: https://stackoverflow.com/questions/8844868/
   Echo/\================================================================================================================
 )
(Set LF=^

%= Linefeed. do not modify. =%)
 Set "Exit.Code=0"
 Set "BEL=(Echo/a|CHOICE /N 2> nul )"
rem [ test if standalone script - if so test if accessed by pipe; reject execution if true ]
 If "%~0" == "%~n0" For %%G in (!cmdcmdline!)Do Echo/%%~G|Findstr.exe /LIC:"/S" > nul 2> nul && (Set "Exit.Code=2" & Goto :Usage)
:retry
 Del /Q "%TEMP%\%~n0_validate.~tmp" 2> nul
 If !Exit.Code! GTR 5 Goto :Usage
 Set "rVar=" & Set "_C=0" & Set "_S=0"
 If "%~2" == "" GOTO :Usage
 Setlocal DISABLEdelayedExpansion & rem ::: dispay occurances of carets [^] in pattern
 Endlocal & Set "Pattern=%~2"
  Set /P "rVar=Input Format for %1:!LF!!Pattern!!LF!!LF!Enter %1: "
 > "%TEMP%\%~n0_validate.~tmp" (Echo/!rVar!)
 If /I "%~3" == "/C" (
  TYPE "%TEMP%\%~n0_validate.~tmp"| findstr.exe /RXC:"%~2" > nul || ( %BEL% & Set /A "Exit.Code+=1" & <nul Set /P"= ^! Invalid value.!LF!" & Goto :Retry)
 ) Else (
  TYPE "%TEMP%\%~n0_validate.~tmp"| findstr.exe /RX "^%~2$" >nul || ( %BEL% & Set /A "Exit.Code+=1" & <nul Set /P"=- ^! Invalid value.!LF!" & Goto :Retry)
 )
 Echo/%1: [!rVar!] Accepted
 ENDLOCAL & Set "%~1=%rVar%"
 Del /Q "%TEMP%\%~n0_validate.~tmp"
 Exit /B 0
:Usage
 Mode 1000
 If "%Exit.Code%" == "0" (Set "Exit.Code=1")
 If "%Exit.Code%" == "2" (
  >> "%TEMP%\%~n0_help.~tmp" (
    Echo/-------------------------------              ^^! pipes not supported ^^!              -------------------------------
    Echo/=================================================================================================================
 ))
If %Exit.Code% GTR 5 (
  >> "%TEMP%\%~n0_help.~tmp" (
   Echo/-------------------------------      ^^! Attempt limit exceeded. Input rejected ^^!   -------------------------------
   Echo/-------------------------------              ^^! pipes not supported ^^!              -------------------------------
   Echo/=================================================================================================================
  )
  Set "Exit.Code=3"
 )
 Type "%TEMP%\%~n0_help.~tmp" | ( More )
 Del /Q "%TEMP%\%~n0_help.~tmp" 2> nul
 %BEL%
ENDLOCAL & exit /B %Exit.Code%
rem ::: [ ------------------------------------------------------------------------------------------------- End GetIn ]

If a function, Usage in your case [A number, maximum value unspecified] would be:

  • Call :GetIn Input "[0123456789]*" If you need to restrict the value to within a given min-max range, place the call within a loop and use LEQ / GTR conditional testing after the call to confirm the value is within the required range, loop back if not
T3RR0R
  • 2,747
  • 3
  • 10
  • 25