Batch file 如何阻止Windows命令解释器在用户输入错误时退出批处理文件执行?
如果我有以下示例代码:Batch file 如何阻止Windows命令解释器在用户输入错误时退出批处理文件执行?,batch-file,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”而不是有效响应,如何阻止批退出?首先,我建议在浏览器中添加书签: (SS64) 您可以通过在命令提示符窗口中运行带有/?作为参数的命令
@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”而不是有效响应,如何阻止批退出?首先,我建议在浏览器中添加书签:
- (SS64)
/?
作为参数的命令来获取每个Windows命令的帮助,例如if/?
,set/?
第二,如果用户应该从几个选项中选择一个,不要使用
set/p
。提示用户输入字符串并将其分配给环境变量时,必须考虑多个事实:
set/p“MyVar=Your choice:“
如果用户有意或错误地按下,则不会修改环境变量MyVar
,只要返回或输入即可。这意味着,如果在用户提示之前未定义环境变量MyVar
,则在用户提示完成后,只要按回车键,环境变量仍然未定义。如果在用户提示之前已经定义了MyVar
,它将保持其值不变,以防用户只按RETURN或ENTER
set/p
。批处理文件编写器无法控制用户真正输入的内容。因此,批处理文件编写器必须考虑到,用户错误地或有意地输入了一个字符串,该字符串可能由于语法错误而导致批处理文件执行的退出,或者它执行的操作与定义的完全不同
@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.
出于调试目的,此批处理文件在命令提示窗口中启动,顶部显示的是echo on
,而不是echo off
第一次运行时,在用户提示下按Just RETURN。Windows命令解释器退出批处理,因为第一个IF条件在执行IF命令之前进行了预处理:
if == 1 goto Label1
显然缺少了第一个论点cmd.exe
遇到此语法错误,并以相应的错误消息退出批处理。原因是缺少环境变量菜单
的定义,该变量在用户提示前未定义,在用户提示后仍未定义
字符串2
在从命令提示符窗口第二次运行批处理文件时输入,批处理文件按预期工作
在同一命令提示窗口中再次运行批处理文件时,在用户提示下按RETURN键。批处理文件再次输出第二条消息。为什么?环境变量菜单
仍然使用该字符串从第二批文件执行中定义,并且在按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.
这已经更好了,因为现在环境变量菜单
总是用值2
预定义的。因此,如果用户不输入任何内容,则跳转到Label2
。此外,变量菜单的上一次运行的值对批处理文件的执行不再有影响
但现在这真的是安全的和故障安全的吗
不,不是。用户仍然可以错误地输入错误的字符串
例如,用户错误地输入了“
而不是2
,这在德国键盘上很容易,因为CapsLock+2或Shift+2会导致输入”
。预处理后的第一个IF命令行现在是:
if """ == "1" goto Label1
这也是一个无效的命令行,由于语法错误导致批处理文件处理退出
假设用户在提示符下输入字符串:
" == "" call dir "%USERPROFILE%\Desktop" & rem
注意:末尾有一个空格
第一个IF条件由Windows命令解释器预处理为:
if "" == "" call dir "%USERPROFILE%\Desktop" & rem " == "1" goto Label1
可以看出,批处理文件现在在两种IF条件下都执行一个根本没有写入批处理文件的命令
如何获得故障安全的用户提示
通过使用延迟环境变量展开,至少在代码周围评估用户输入字符串
@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.
现在,用户输入字符串不再修改Windows命令解释器执行的命令行。因此,由于用户输入引起的语法错误而退出批处理文件处理不再可能。批处理文件从不执行批处理文件中未写入的命令
第三,对于简单的选择菜单,有比set/p
更好的命令
用户不再有自由输入批处理文件编写器未定义的内容。用户按下1、2、e或Shift+e后,批处理文件将立即继续。除Ctrl+C外,选项将忽略所有其他内容
动态变量ERRORLEVEL
在choice
终止后,有三个选项,几乎总是在1到3范围内的值,返回1到3作为调用cmd.exe的退出代码。例外情况是罕见的使用案例,即批处理文件的用户在提示下按Ctrl+C,然后回答下一个提示终止批处理作业(Y/N)?
的cmd.exe
带有N。在这种情况下,动态变量ERRORLEVEL
的值为0
,这就是为什么如果不是ERRORLEVEL 1,则转到main menu
来处理这个非常特殊的用例的原因
注意:如果错误级别X
表示如果大于或等于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%
:Label0
rem The user pressed Ctrl+C and on next prompt N and
rem so made no choice. Prompt the user once again.
goto MainMenu
:Label1
@echo Option 1 was chosen, fine.
goto :EOF
:Label2
@echo Option 2 was chosen, okay.
goto :EOF
:Label3
命令的使用选项
@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%
:Label0
rem The user pressed Ctrl+C and on next prompt N and
rem so made no choice. Prompt the user once again.
goto MainMenu
:Label1
@echo Option 1 was chosen, fine.
goto :EOF
:Label2
@echo Option 2 was chosen, okay.
goto :EOF
:Label3