How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?

后端 未结 1 977
梦谈多话
梦谈多话 2020-11-22 05:06

If I have the following example code:

@echo off
:menu
cls
echo 1. win
echo 2. lose
set /p menu=
goto %menu%
pause>nul

:win
cls
echo yay lolz
pause>nul         


        
相关标签:
1条回答
  • 2020-11-22 05:45

    First, I suggest to bookmark in your browser:

    • Microsoft's command-line reference
    • An A-Z Index of the Windows CMD command line (SS64)

    You can get help for every Windows command by running in a command prompt window the command with /? as parameter, for example if /?, set /?, ...


    Second, don't use set /p if the user should choose one of several options. There are multiple facts which must be taken into account on prompting user for entering a string and assigning it to an environment variable:

    1. On usage of set /P "MyVar=Your choice: " the environment variable MyVar is not modified if the user presses intentionally or by mistake just RETURN or ENTER. This means if the environment variable MyVar is not defined already before the user prompt, it is still not defined after user prompt finished with just hitting key RETURN. And if MyVar is defined already before user prompt, it keeps its value unmodified in case of user presses just RETURN or ENTER.

    2. The user has the freedom to type any string on being prompted with set /P. The batch file writer has no control on what the user really enters. So the batch file writer must take into account that the user enters by mistake or intentionally a string which could result in an exit of batch file execution because of a syntax error or it does something completely different as it is defined for.

    A simple example:

    @echo on
    :MainMenu
    @set /P "menu=Your choice: "
    if %menu% == 1 goto Label1
    if %menu% == 2 goto Label2
    goto MainMenu
    
    :Label1
    @echo Option 1 was chosen, fine.
    goto :EOF
    
    :Label2
    @echo Option 2 was chosen, okay.
    

    This batch file with echo on instead of echo off at top is started from within a command prompt window for debugging purposes.

    Just RETURN is pressed on user prompt on first run. Windows command interpreter exits batch processing because first IF condition is preprocessed before execution of IF command to:

    if  == 1 goto Label1
    

    There is obviously missing the first argument. cmd.exe encounters this syntax error and exits batch processing with an appropriate error message. The reason is environment variable menu is not defined before user prompt and is still not defined after user prompt.

    On second run of the batch file from within command prompt window 2 is entered and the batch file works as expected.

    On third run of the batch file from within same command prompt window again just RETURN is pressed on user prompt. The batch file outputs again the second message. Why? The environment variable menu is still defined from second batch file execution with that string and the variable was not modified on pressing RETURN.

    Okay, let us modify the example batch file to:

    @echo on
    :MainMenu
    @set "menu=2"
    @set /P "menu=Your choice: "
    if "%menu%" == "1" goto Label1
    if "%menu%" == "2" goto Label2
    goto MainMenu
    
    :Label1
    @echo Option 1 was chosen, fine.
    goto :EOF
    
    :Label2
    @echo Option 2 was chosen, okay.
    

    This is already better as now environment variable menu is always predefined with value 2. So if the user enters nothing, a jump to Label2 is done. Also the value of previous run of variable menu has no effect anymore on execution of batch file.

    But is that really secure and fail safe now?

    No, it isn't. The user still can enter by mistake a wrong string.

    For example the user enters by mistake " instead of 2 which is easy on German keyboards as CapsLock+2 or Shift+2 results in entering ". The first IF command line after preprocessing is now:

    if """ == "1" goto Label1
    

    And this is again an invalid command line resulting in an exit of batch file processing because of a syntax error.

    Let us assume a user enters on prompt the string:

    " == "" call dir "%USERPROFILE%\Desktop" & rem 
    

    Note: There is a space at end.

    The first IF condition is preprocessed by Windows command interpreter to:

    if "" == "" call dir "%USERPROFILE%\Desktop"   & rem " == "1" goto Label1
    

    It can be seen that the batch files executes now a command not written in the batch file at all on both IF conditions.

    How to get a user prompt fail safe and secure?

    By using delayed environment variable expansion at least around code evaluating the user input string.

    @echo on
    :MainMenu
    @setlocal EnableDelayedExpansion
    @set "menu=2"
    @set "Label=MainMenu"
    @set /P "menu=Your choice: "
    if "!menu!" == "1" set "Label=Label1"
    if "!menu!" == "2" set "Label=Label2"
    endlocal & goto %Label%
    
    :Label1
    @echo Option 1 was chosen, fine.
    goto :EOF
    
    :Label2
    @echo Option 2 was chosen, okay.
    

    Now the user input string does not modify anymore the command lines executed by Windows command interpreter. So an exit of batch file processing because of a syntax error caused by user input is not possible anymore. And the batch file never executes commands not written in batch file.


    Third, there is a better command than set /P for a simple choice menu – CHOICE.

    @echo off
    :MainMenu
    cls
    echo/
    echo    1 ... Option 1
    echo    2 ... Option 2
    echo    E ... Exit
    echo/
    %SystemRoot%\System32\choice.exe /C 12E /N /M "Your choice: "
    if errorlevel 3 goto :EOF
    if errorlevel 2 goto Label2
    
    @echo Option 1 was chosen, fine.
    goto :EOF
    
    :Label2
    @echo Option 2 was chosen, okay.
    

    The user has no freedom anymore to enter something not defined by batch file writer. The batch file continues immediately after the user has pressed either 1, 2, e or Shift+E. Everything else is ignored by choice with exception of Ctrl+C.

    The environment variable ERRORLEVEL has with three options always a value in range 1 to 3 after choice terminated with returning 1 to 3 as exit code to calling cmd.exe.

    Note: if errorlevel X means IF GREATER OR EQUAL X. So it is always necessary to start with highest possible exit code of command choice.

    As the exit code assigned to ERRORLEVEL is well known, it is possible on larger menus to optimize the code further by using appropriate labels:

    @echo off
    :MainMenu
    cls
    echo/
    echo    1 ... Option 1
    echo    2 ... Option 2
    echo    E ... Exit
    echo/
    %SystemRoot%\System32\choice.exe /C 12E /N /M "Your choice: "
    goto Label%ERRORLEVEL%
    
    :Label1
    @echo Option 1 was chosen, fine.
    goto :EOF
    
    :Label2
    @echo Option 2 was chosen, okay.
    goto :EOF
    
    :Label3
    

    The usage of command CHOICE can make choice menus very simple to code.


    Fourth, see also answer on Where does GOTO :EOF return to?

    0 讨论(0)
提交回复
热议问题