Why is string comparison after prompting user for a string/option not working as expected?

后端 未结 1 1766
醉酒成梦
醉酒成梦 2021-01-25 12:48

Is this the proper way to write these lines of batch code with the exceptions of spaces and unneeded percent signs?

:name
cls
echo now that we\'ve got your color         


        
1条回答
  •  被撕碎了的回忆
    2021-01-25 12:55

    The main mistake is a simple syntax issue:

    Environment variables are defined with just specifying the variable name without percent signs and with no space character before the equal sign.

    So wrong is

    set /p %player% =
    set /p %namechoice% =
    

    because those two lines are expanded during preprocessing phase before really executing the command SET to

    set /p  =
    set /p  =
    

    in case of environment variables player and namechoice are not already defined. See Why is no string output with 'echo %var%' after using 'set var = text' on command line? for details about how to define an environment variable right. It explains also why the space character left to equal sign on variable definition becomes part of the variable name which is nearly always unwanted by batch file writer.

    Such simple syntax issues can be easily seen on running a batch file without @echo off at top of the batch file or with this line modified to @echo on or commented out with ::@echo off (invalid label) or rem @echo off (remark command) from within a command prompt window by entering name of the batch file with full path in double quotes instead of double clicking on the batch file.

    What makes the difference?

    1. With @echo off the command lines are not printed into the console window after preprocessing (expanding environment variables) before really executing them. This is the wanted behavior when batch file development finished. But during development and testing of a batch file it is definitely better to get displayed what is really executed by Windows command interpreter to find coding mistakes.

    2. On double clicking a batch file cmd.exe is started to execute the batch file with option /C for closing the console window automatically when batch file execution terminated independent on success or error of execution. This makes it not possible to see for example syntax errors output by Windows command interpreter which result in an immediate exit of batch file execution. Therefore it is advisable during batch file development to run it from within a manually opened command prompt window as in this case cmd.exe is started with option /K to keep the console window open even after batch processing finished, except the batch file uses command exit without parameter /B. This makes it possible to see also the error message of an error which caused an unexpected exit of batch processing.

    Later when batch file works as expected, the first line can be @echo off again and of course the batch file can be started with a double click. But during batch file development it is definitely better to always run the batch file from within a command prompt window. The up/down arrow keys can be used to scroll through the list of entered strings which makes it also possible to re-enter for example the player name easily again.

    Here is the batch code rewritten with several improvements and comments:

    @echo off
    setlocal EnableExtensions EnableDelayedExpansion
    
    rem Define a too long player name before prompting the user for the player
    rem name. This too long player name is kept in case of user hits just the
    rem key RETURN or ENTER without entering anything at all. Then test for
    rem entered name has not more than 6 characters. Delayed expansion is used
    rem as the user could enter characters like " ! % ... which would in further
    rem batch code execution result in exiting batch processing because of syntax
    rem error or in unexpected behavior on referencing player name with expansion
    rem before running the command.
    
    :PromptForName
    cls
    echo Now that we've got your color figured out, what about your name?
    echo Simply type the name you want for your character into the space
    echo below (6 char max).
    echo/
    set "Player=No name entered"
    set /P "Player=Player name: "
    if not "!Player:~6!" == "" goto PromptForName
    
    echo/
    echo/
    echo    1) yes
    echo    2) no
    echo/
    choice /C:12 /N "So you want your name to be !player!? "
    if errorlevel 2 goto PromptForName
    
    if /I "!player!" == "%USERNAME%" goto GameStart
    
    echo Surprise
    endlocal
    goto :EOF
    
    :GameStart
    echo/
    echo Okay !Player!, let's play^^!
    
    rem Wait 3 seconds using PING instead of TIMEOUT before exiting the
    rem batch file because the command TIMEOUT does not exist on Windows XP.
    %SystemRoot%\System32\ping.exe 127.0.0.1 -n 4 >nul
    
    endlocal
    

    The comment at top explains why the environment variable Player is defined with value No name entered. The batch user has the freedom to hit just RETURN or ENTER without entering anything at all or hits by mistake one of those 2 keys before entering a name. In this case the environment variable Player is either still not defined if not defined before, or it keeps its current value if already defined before. It is not good if the user enters nothing and the environment variable Player is not defined in this case. Therefore the player name is predefined with an invalid name.

    The length of the entered player name is also tested on being too long.

    And the string entered by the user could contain batch syntax critical characters like a double quote, a percent sign, a redirection operator character (angle bracket, pipe), an ampersand, or with delayed expansion enabled an exclamation mark. To prevent an exit of batch processing caused by a syntax error by entered player name on using environment variable expansion before command line execution, the environment variable Player is referenced everywhere with usage of delayed expansion enabled at top of the batch file.

    For printing a blank line it is better to use echo/ instead of echo. because echo. could fail and is a little bit slower because of Windows command interpreter searches for a file matching the pattern echo.* as documented in DosTips forum article ECHO. FAILS to give text or blank line - Instead use ECHO/.

    The command CHOICE is much better than set /P VariableName=Prompt text if the user has to enter specific keys. The command CHOICE does not allow that the user enters something not wanted by batch file writer and is therefore much safer for a choice menu.

    The account name of current user referenced with %USERNAME% could contain also a space character. Therefore it is highly recommended to enclose the entire string containing %USERNAME% always in double quotes.

    "%USERNAME%" on right side of a string comparison requires that the string on left side is also enclosed in double quotes because command IF compares the two strings with including the double quotes.

    For that reason the condition

    if /I !player! == "%USERNAME%"
    

    would be only true if the batch file user would have entered the player name with double quotes which is very unlikely. The double quotes must be also used on left side.

    The number of space characters around the two compared strings enclosed in double quotes or not enclosed in double quotes does not matter.

    Executing in a command prompt window the following batch file

    @echo on
    @setlocal EnableExtensions EnableDelayedExpansion
    @set "Player=<|>"
    if /I "!Player!"=="%SystemRoot%" echo Strings are equal.
    if /I "!Player!"  ==  "%WinDir%" echo Strings are equal.
    if /I   "!Player!" ==  "%Player%" echo Strings are equal.
    if /I "!Player!"==   "!Player!" echo Strings are equal.
    if   /I   !Player!  ==  !Player! echo Strings are equal.
    @endlocal
    

    results in the output

    if /I "!Player!" == "C:\WINDOWS" echo Strings are equal.
    
    if /I "!Player!" == "C:\WINDOWS" echo Strings are equal.
    
    if /I "!Player!" == "<|>" echo Strings are equal.
    Strings are equal.
    
    if /I "!Player!" == "!Player!" echo Strings are equal.
    Strings are equal.
    
    if /I !Player! == !Player! echo Strings are equal.
    Strings are equal.
    

    It can be seen that the space characters around comparison operator == do not matter on execution of command IF. The Windows command processor formats the command lines pretty before executing the IF commands.

    But a space character in a string to compare requires the usage of double quotes because otherwise an exit of batch processing occurs most likely because of a syntax error on batch file execution.

    Note: The equal operator == of command IF is handled different than the assignment operator = of command SET. Don't mix them.

    For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

    • choice /?
    • cls /?
    • echo /?
    • endlocal /?
    • goto /?
    • if /?
    • ping /?
    • rem /?
    • set /?
    • setlocal /?

    And see also the Microsoft article Using command redirection operators for an explanation of >nul.

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