如何阻止Windows命令解释器退出用户输入错误的批处理文件? [英] How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?

查看:71
本文介绍了如何阻止Windows命令解释器退出用户输入错误的批处理文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有以下示例代码:

@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"而不是有效的响应,如何停止批量退出?

解决方案

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

通过在命令提示符窗口中运行以/?作为参数的命令,例如if /?set /?,...

,可以获得有关每个Windows命令的帮助.

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

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

  2. 在按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是从命令提示符窗口中启动的,用于调试.

只需在第一次运行时在用户提示符上按 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.
goto :EOF

:Label2
@echo Option 2 was chosen, okay.

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

但这真的很安全,现在失败了吗?

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

例如,用户错误地输入了"而不是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更好的命令– 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.

用户不再具有输入批处理文件编写器未定义的内容的自由.用户按下 1 2 e Shift + E 后,批处理文件将立即继续.除了 Ctrl + C 之外,其他所有内容均被choice忽略.

choice终止后,环境变量ERRORLEVEL具有三个选项,其值始终在1到3之间,并返回1到3作为调用cmd.exe的退出代码.

注意:if errorlevel X表示大于或等于X .因此,总是有必要从命令choice的最高退出代码开始.

由于分配给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

使用命令 CHOICE 可以使选择菜单的编码非常简单.


第四,另请参见 GOTO:EOF返回哪里?

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

:lose
cls
echo really?
pause>nul

How do I stop the batch from quitting if I type "test" instead of a valid response?

First, I suggest to bookmark in your browser:

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?

这篇关于如何阻止Windows命令解释器退出用户输入错误的批处理文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆