如何阻止Windows命令解释程序在错误的用户输入上退出批处理文件?

时间:2018-04-14 15:49:23

标签: batch-file

如果我有以下示例代码:

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

:win
cls
echo yay lolz
pause>nul

:lose
cls
echo really?
pause>nul

如果我输入“test”而不是有效的回复,如何停止批量退出?

1 个答案:

答案 0 :(得分:2)

首先,我建议您在浏览器中添加书签:

您可以通过在命令提示符窗口中运行/?作为参数的命令来获取每个Windows命令的帮助,例如if /?set /?,...

其次,如果用户应选择其中一个选项,请不要使用set /p。在提示用户输入字符串并将其分配给环境变量时,必须考虑多个事实:

  1. 在使用set /P "MyVar=Your choice: "时,如果用户有意或者错误地按下 RETURN ENTER MyVar >。这意味着如果在用户提示之前尚未定义环境变量MyVar,则在用户提示仅按 RETURN 键完成后仍未定义环境变量MyVar。如果在用户提示之前已经定义了set /P,那么在用户只按 RETURN ENTER 的情况下,它会保持其值不被修改。

  2. 用户可以自由键入@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. 提示的任何字符串。批处理文件编写器无法控制用户真正输入的内容。因此批处理文件编写者必须考虑到用户错误地输入或故意输入一个字符串,这可能导致批处理文件执行退出,因为语法错误或者它的定义完全不同。

  3. 一个简单的例子:

    echo on

    此顶部带有echo off而不是if == 1 goto Label1 的批处理文件是从命令提示符窗口中启动的,用于调试目的。

    首次运行时,在用户提示符下按 RETURN 。 Windows命令解释程序退出批处理,因为在执行 IF 命令之前,首先 IF 条件被预处理:

    cmd.exe

    显然缺少第一个参数。 menu遇到此语法错误并退出批处理并显示相应的错误消息。原因是环境变量2未在用户提示之前定义,并且在用户提示后仍未定义。

    在命令提示符窗口menu内第二次运行批处理文件时,批处理文件按预期工作。

    在同一命令提示符窗口中第三次运行批处理文件时,在用户提示符下按 RETURN 。批处理文件再次输出第二条消息。为什么?环境变量@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. 仍然是使用该字符串从第二个批处理文件执行定义的,并且在按 RETURN 时未修改变量。

    好的,我们将示例批处理文件修改为:

    menu

    这已经更好了,因为现在环境变量2始终预定义为值Label2。因此,如果用户没有输入任何内容,则跳转到menu。此外,上一次运行变量"的值不再对批处理文件的执行产生影响。

    但这真的是安全的,现在也不安全吗?

    不,不是。用户仍然可以错误输入错误的字符串。

    例如,用户输入错误2而不是",这在德语键盘上很容易,因为 CapsLock + 2 Shift + 2 导致输入if """ == "1" goto Label1 。现在预处理后的第一个 IF 命令行是:

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

    这又是一个无效的命令行,由于语法错误导致批处理文件处理退出。

    让我们假设用户在提示符下输入字符串:

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

    注意:最后有一个空格。

    第一个 IF 条件由Windows命令解释程序预处理为:

    @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.
    

    可以看出批处理文件现在在 IF 条件下执行完全没有在批处理文件中写入的命令。

    如何让用户提示失败安全?

    至少在评估用户输入字符串的代码周围使用延迟环境变量扩展。

    set /P

    现在,用户输入字符串不再修改Windows命令解释程序执行的命令行。因此,由于用户输入引起的语法错误,不再可能退出批处理文件处理。并且批处理文件永远不会执行未在批处理文件中写入的命令。

    第三,对于一个简单的选择菜单,有一个比@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. 更好的命令 - CHOICE

    choice

    用户不再有自由输入批处理文件编写器未定义的内容。用户按下 1 2 e Shift + E 后立即继续批处理文件。除{kbd> Ctrl + C 外,ERRORLEVEL忽略其他所有内容。

    环境变量choicecmd.exe终止后返回1到3作为退出代码调用if errorlevel X时,有三个选项始终为1到3范围内的值。

    注意choice表示 IF GREATER OR EQUAL X 。因此,始终需要从命令ERRORLEVEL的最高退出代码开始。

    由于分配给@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 的退出代码众所周知,因此可以在较大的菜单上使用适当的标签进一步优化代码:

    {{1}}

    使用命令 CHOICE 可以使选择菜单变得非常简单。

    第四,另见Where does GOTO :EOF return to?

    的回答