@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”而不是有效响应时退出?
@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
我建议在浏览器中收藏以下内容:
可以通过在命令提示符窗口中带有/?
参数来运行每个Windows命令以获取帮助,例如if /?
,set /?
等。执行help
命令会输出一个不完整的Windows命令列表及其简要说明。
如果用户需要从多个选项中选择一个,则建议不使用set /p
。在提示用户输入字符串并将其分配给环境变量时,必须考虑多个因素:
如果用户故意或错误地仅按下RETURN或ENTER键,则在使用set /P "MyVar=Your choice: "
时并不会修改环境变量MyVar
。这意味着如果环境变量MyVar
在用户提示之前未定义,则在用户按下RETURN键后仍未定义。如果在用户提示之前已经定义了MyVar
,则在用户仅按下RETURN或ENTER键的情况下保持其值不变。根据内部cmd.exe命令设置的ERRORLEVEL值是什么?文档,命令SET在用户没有输入字符串时将以错误值1
退出。
用户可以自由地在使用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.
exit /B
:Label2
@echo Option 2 was chosen, okay.
这个批处理文件在顶部使用echo on
而不是echo off
,是为了调试目的而从命令提示符窗口启动。
第一次运行时,只需按下用户提示上的RETURN。 Windows命令解释器在执行IF命令之前对第一个IF条件进行预处理,然后退出批处理文件处理:
if == 1 goto Label1
很明显缺少第一个参数。cmd.exe
因为此语法错误而退出批处理,并输出适当的错误信息。原因是环境变量menu
在用户提示之前没有定义,且在用户提示后仍未定义。
在命令提示符窗口内第二次运行批处理文件时输入了字符串2
,批处理文件按预期工作。
在同一命令提示符窗口内再次运行批处理文件的第三次,只需按下RETURN键即可。批处理文件再次输出第二条消息。为什么?因为环境变量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.
exit /B
:Label2
@echo Option 2 was chosen, okay.
menu
始终预定义为值2
。所以如果用户没有输入任何内容,则会跳转到Label2
。此外,先前运行的变量menu
的值对批处理文件的执行不再产生影响。1
,并仅在这种情况下使用默认值定义环境变量,方法如下:@set /P "menu=Your choice: " || set "menu=2"
使用Windows批处理文件的单行多个命令描述了条件执行运算符||
,只有在第一个执行提示命令set /P "menu=Your choice: "
以不等于0
的退出代码退出时才运行第二个命令set "menu=2"
,这通常发生在用户没有输入任何内容时。
感谢aschipfl做出的贡献。
但是现在它真的安全可靠吗?
不,它并不安全可靠。用户仍然可能会错误地输入错误的字符串。
例如,用户误输入"
而不是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
@echo on
:MainMenu
@setlocal EnableDelayedExpansion
@set "Label=MainMenu"
@set /P "menu=Your choice: " || set "menu=2"
if "!menu!" == "1" set "Label=Label1"
if "!menu!" == "2" set "Label=Label2"
endlocal & goto %Label%
:Label1
@echo Option 1 was chosen, fine.
exit /B
:Label2
@echo Option 2 was chosen, okay.
set /P
更好的命令 - 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 exit /B
if errorlevel 2 goto Label2
if not errorlevel 1 goto MainMenu
@echo Option 1 was chosen, fine.
exit /B
:Label2
@echo Option 2 was chosen, okay.
choice
会忽略其他所有内容。ERRORLEVEL
在choice
终止时几乎总是有三个选项的值,范围为1到3,并将1到3作为退出代码返回给调用cmd.exe
。例外情况是批处理文件的用户在提示符上按下Ctrl+C并用N回答cmd.exe
的下一个提示符Terminate batch job (Y/N)?
。在这种情况下,动态变量ERRORLEVEL
的值为0
,这就是if not errorlevel 1 goto MainMenu
处理这个非常特殊的用例的原因。if errorlevel X
表示如果大于或等于X。因此,始终需要从命令choice
的可能最高的退出代码开始。ERRORLEVEL
的退出代码是众所周知的,因此在更大的菜单中,可以通过使用适当的标签进一步优化代码。@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ERRORLEVEL="
: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.
exit /B
:Label2
@echo Option 2 was chosen, okay.
exit /B
:Label3
ERRORLEVEL
的环境变量,否则将防止使用% ERRORLEVEL%
语法访问命令CHOICE的退出代码来使用动态变量ERRORLEVEL
的当前值。
注意:只有选择菜单命令行不在以(
开始并以匹配)
结尾的命令块内部时,才能使用goto Label%ERRORLEVEL%
。exit /B
进行了解释。
Choice
而不是Set /P
。 - Compo