为什么这个批处理脚本会意外终止?

为什么这个批处理脚本会意外终止?

%CHECKCONTINUE%如果第 13 行未输入任何内容并赋值给空值,此批处理脚本就会终止SET /p CHECKCONTINUE=Okay to continue? (y/n):,这是为什么?

@ECHO OFF
SETLOCAL
TITLE Registry restore script
REM Restores registry settings and disables the cloud

SET %CHECKCONTINUE%=

:listaction
ECHO I'm about to...
ECHO 1.) Remove the registry data that specifies settings for TF2
ECHO 2.) Forcibly disable Steam Cloud.
ECHO.
SET /p CHECKCONTINUE=Okay to continue? (y/n): 

REM No?
IF %CHECKCONTINUE%==n GOTO exit
IF %CHECKCONTINUE%==no GOTO exit

REM Yes?
IF %CHECKCONTINUE%==y GOTO start
IF %CHECKCONTINUE%==yes GOTO start

REM Did they put something else?
IF DEFINED %CHECKCONTINUE% GOTO loop-notvalid

REM Did they not put anything at all?
IF NOT DEFINED %CHECKCONTINUE% GOTO loop-noreply

:start
REM Delete application specific data
REG DELETE HKEY_CURRENT_USER\Software\Valve\Source\tf\Settings /f
REG DELETE HKEY_CURRENT_USER\Software\Valve\Steam\Apps\440 /f

REM Disable Steam Cloud for TF2
REG ADD HKEY_CURRENT_USER\Software\Valve\Steam\Apps\440 /v Cloud /t REG_DWORD /d "0x0" /f

:exit
ENDLOCAL
EXIT

:loop-notvalid
ECHO.
ECHO That's not a valid reply. Try again.
ECHO.
SET %CHECKCONTINUE%=
GOTO listaction

:loop-noreply
ECHO.
ECHO You must enter a reply.
ECHO.
SET %CHECKCONTINUE%=
GOTO listaction

答案1

首先,在多行中你有:

SET %CHECKCONTINUE%=

这不会修改CHECKCONTINUE,但会利用其价值作为变量名。

更改为:

SET CHECKCONTINUE=

如果您将其立即移到上面,那会更好set /p ...- 这样您只需要它一次。


if defined也只接受一个变量名,因此

IF DEFINED %CHECKCONTINUE% GOTO loop-notvalid

你应该使用:

IF DEFINED CHECKCONTINUE GOTO loop-notvalid

这行同样适用:

IF NOT DEFINED %CHECKCONTINUE% GOTO loop-noreply

但是,它可以缩短为:

GOTO loop-noreply

如果定义了变量,则执行永远不会到达此行(if defined ...上面)


我本来会这样写:

@echo off & setlocal
title Registry restore script
:: Restores registry settings and disables the Cloud

:menu
echo I'm about to...
echo 1) Remove the registry data that specifies settings for TF2
echo 2) Forcibly disable Steam Cloud.
echo.
set check=
set /p check=Okay to continue? (y/n)
:: /i means case-insensitive comparison
if /i %check%==y goto :start
if /i %check%==yes goto :start
if /i %check%==n goto :EOF
if /i %check%==no goto :EOF
:: On empty response, pick the safest option as default
if not defined check goto :EOF

goto :loop-invalid

:start
:: Delete application specific data
reg delete HKCU\Software\Valve\Source\tf\Settings /f
reg delete HKCU\Software\Valve\Steam\Apps\440 /f

:: Disable Steam Cloud for TF2
reg add HKCU\Software\Valve\Steam\Apps\440 /v Cloud /t REG_DWORD /d "0x0" /f

:loop-invalid
echo.
echo Not a valid answer.
goto :menu

答案2

扩展 gravity 的精彩答案:

首先,回答您的问题:当 %CHECKCONTINUE% 被赋予空值时,批处理脚本为什么会终止?

问题是,在第 16 行,你这样做:

if %CHECKCONTINUE%==n GOTO exit

由于CHECKCONTINUE是“未定义”,因此其计算结果为“空”字符串,因此第 16 行的语句实际上是在执行:

if ==n GOTO exit

这是一个无效的语句,因为 的左侧没有任何内容"=="。因此,当批处理脚本尝试执行格式不正确的语句时,它将终止:

C:\>script.cmd
I'm about to...
1.) Remove the registry data that specifies settings for TF2
2.) Forcibly disable Steam Cloud.

Okay to continue? (y/n): <ENTER key pressed>
GOTO was unexpected at this time.

C:\>

如果有人输入包含空格的内容,您也会遇到类似的问题:

C:\>script.cmd
I'm about to...
1.) Remove the registry data that specifies settings for TF2
2.) Forcibly disable Steam Cloud.

Okay to continue? (y/n): Yes please
please==n was unexpected at this time.

C:\>

为了解决这个问题,您应该使用双引号将术语括起来,如下所示:

if "%CHECKCONTINUE%"=="n" GOTO :exit

如果使用的变量可能是“空”或者它们可能嵌入空格,则这是必要的,但在使用 进行评估时始终使用双引号是个好主意"=="

注意:某些错误(例如上面带有 和 的错误"if""=="是“致命”错误,会导致批处理脚本的执行立即停止。其他错误(例如下面带有 的错误"set")是“非致命”错误。对于“非致命”错误,不会执行带有错误的语句,而是显示错误消息,批处理脚本将从下一个语句开始继续运行

接下来,正如 grawity 指出的:

set %CHECKCONTINUE%=

这不会修改 CHECKCONTINUE,但使用其值作为变量名。

同样,如果CHECKCONTINUE是“未定义”,它将被计算为一个“空”字符串,因此该语句实际上是在执行:

set =

这也是一个无效的语句,因为 的左侧没有任何内容"="

还有这些行:

if defined %CHECKCONTINUE% GOTO loop-notvalid
if not defined %CHECKCONTINUE% GOTO loop-noreply

"if defined"(和"if not defined")需要变量名,而不是变量值。如果CHECKCONTINUE未定义,%CHECKCONTINUE%则计算结果为空字符串,这些语句实际上是:

if defined  GOTO loop-notvalid
if not defined  GOTO loop-noreply

这里,"if defined"(和"if not defined")将检查名为的变量是否GOTO已定义。

此外,对于这 3 行,如果CHECKCONTINUE实际上已定义,"set"并且"if defined"将对变量的进行操作"value",而不是"name"变量本身的。因此,如果CHECKCONTINUE已经有一个值"y",那么:

set %CHECKCONTINUE%=
if defined %CHECKCONTINUE% goto loop-notvalid
if not defined %CHECKCONTINUE% goto loop-noreply

实际上会被视为:

set y=
if defined y goto loop-notvalid
if not defined y goto loop-noreply

示例“script.cmd”:

@set "CHECKCONTINUE="

@rem ## CHECKCONTINUE="%CHECKCONTINUE%" (undefined/empty). 
@rem ## 05: set %CHECKCONTINUE%=
set %CHECKCONTINUE%=

@echo This doesn't set the value of of the variable named "CHECKCONTINUE". 
@echo Since no variable name is actually specified, it is an error. 



@set "CHECKCONTINUE=yes"
@set "yes=something"

@rem ## CHECKCONTINUE="%CHECKCONTINUE%" and the value of the variable named "yes"="%yes%" 
@rem ## 17: set %CHECKCONTINUE%=
set %CHECKCONTINUE%=

@echo This doesn't set the value of the variable named "CHECKCONTINUE". 
@echo Since CHECKCONTINUE="%CHECKCONTINUE%", it sets the value of the variable named 
@echo "%CHECKCONTINUE%". No error is shown because the statement is valid. 
@echo It could have been a problem (well, at least a big annoyance) if 
@echo CHECKCONTINUE had the value: "path". The statement 
@echo should be: set "CHECKCONTINUE=" 

@rem ## 27: echo CHECKCONTINUE still has the value: "%CHECKCONTINUE%"
@echo CHECKCONTINUE still has the value: "%CHECKCONTINUE%"

@rem ## 30: echo and the variable named "%CHECKCONTINUE%" is now empty="%yes%"
@echo and the variable named "%CHECKCONTINUE%" is now empty="%yes%"



@set "yes="
@set "CHECKCONTINUE="
@set "echo=something"

@rem ## CHECKCONTINUE="%CHECKCONTINUE%" (undefined) and the value of the variable 
@rem ## named "echo"="%echo%". 
@rem ## 41: if defined %CHECKCONTINUE% echo Variable is defined.
if defined %CHECKCONTINUE% echo Variable is defined.

@echo This doesn't check if the variable named "CHECKCONTINUE" is defined. 
@echo Since it's "empty", it is skipped (well, there is nothing there to 
@echo "skip") and "if defined" is checking the next word (which is "echo"). 
@echo What's left is: if defined echo Variable is defined. 
@echo So, it checks if a variable named "echo" is defined (which it is). 
@echo Since "if defined" has checked a variable named "echo", it then tries 
@echo to execute the rest of the line starting with the word "Variable", 
@echo as a command. This fails and is an error. The statement 
@echo should be: if defined CHECKCONTINUE echo Variable is defined. 



@set "echo="

@rem ## CHECKCONTINUE="%CHECKCONTINUE%" (undefined) and "echo"="%echo%" (undefined). 
@rem ## 59: if not defined %CHECKCONTINUE% echo The-variable-is-not-defined.
if not defined %CHECKCONTINUE% echo The-variable-is-not-defined.

@echo Similar: Since "if not defined" has checked a variable named "echo" 
@echo (which is "undefined"), it then tries to execute the rest of the 
@echo line: "The-variable-is-not-defined." as a command. This fails and is 
@echo an error. The statement 
@echo should be: if not defined CHECKCONTINUE echo The-variable-is-not-defined. 



@set "echo=something"

@rem ## CHECKCONTINUE="%CHECKCONTINUE%" (undefined) and "echo"="%echo%". 
@rem ## 73: if defined %CHECKCONTINUE% echo Verify this.
if defined %CHECKCONTINUE% echo Verify this.

@echo Again, similar: Since "if defined" has checked a variable named 
@echo "echo", it then tries to execute the rest of the line starting with 
@echo the word: "Verify" as a command. This happens to be a valid command 
@echo but it also fails because of an incorrect parameter for the command. 
@echo The statement should be: if defined CHECKCONTINUE echo Verify this. 



@set "echo="

@set "CHECKCONTINUE=yes"
@set "yes="

@rem ## CHECKCONTINUE="%CHECKCONTINUE%" and the variable named "yes"="%yes%" (undefined). 
@rem ## 90: if not defined %CHECKCONTINUE% echo CHECKCONTINUE is not defined.
if not defined %CHECKCONTINUE% echo CHECKCONTINUE is not defined.

@echo Here "CHECKCONTINUE" is defined, but "if not defined" still doesn't 
@echo check if the variable named "CHECKCONTINUE" is defined. Since 
@echo CHECKCONTINUE has a value of "%CHECKCONTINUE%", "if not defined" is 
@echo checking if a variable named "%CHECKCONTINUE%" is defined (which it isn't). 
@echo This causes "if not defined" to proceed and echo the message when 
@echo that's probably not what was intended. The statement 
@echo should be: if not defined CHECKCONTINUE echo CHECKCONTINUE is not defined.

运行“script.cmd”你会得到:

## CHECKCONTINUE="" (undefined/empty). 
## 05: set %CHECKCONTINUE%=

    C:\>set =
    The syntax of the command is incorrect.

This doesn't set the value of of the variable named "CHECKCONTINUE". 
Since no variable name is actually specified, it is an error. 



## CHECKCONTINUE="yes" and the value of the variable named "yes"="something" 
## 17: set %CHECKCONTINUE%=

    C:\>set yes=

This doesn't set the value of the variable named "CHECKCONTINUE". 
Since CHECKCONTINUE="yes", it sets the value of the variable named 
"yes". No error is shown because the statement is valid. 
It could have been a problem (well, at least a big annoyance) if 
CHECKCONTINUE had the value: "path". The statement 
should be: set "CHECKCONTINUE=" 

## 27: echo CHECKCONTINUE still has the value: "%CHECKCONTINUE%"

    CHECKCONTINUE still has the value: "yes"

## 30: echo and the variable named "yes" is now empty="%yes%"

    and the variable named "yes" is now empty=""



## CHECKCONTINUE="" (undefined) and the value of the variable 
## named "echo"="something". 
## 41: if defined %CHECKCONTINUE% echo Variable is defined.

    C:\>if defined echo Variable is defined.
    'Variable' is not recognized as an internal or external command,
    operable program or batch file.

This doesn't check if the variable named "CHECKCONTINUE" is defined. 
Since it's "empty", it is skipped (well, there is nothing there to 
"skip") and "if defined" is checking the next word (which is "echo"). 
What's left is: if defined echo Variable is defined. 
So, it checks if a variable named "echo" is defined (which it is). 
Since "if defined" has checked a variable named "echo", it then tries 
to execute the rest of the line starting with the word "Variable", 
as a command. This fails and is an error. The statement 
should be: if defined CHECKCONTINUE echo Variable is defined. 



## CHECKCONTINUE="" (undefined) and "echo"="" (undefined). 
## 59: if not defined %CHECKCONTINUE% echo The-variable-is-not-defined.

    C:\>if not defined echo The-variable-is-not-defined.
    'The-variable-is-not-defined.' is not recognized as an internal or external command,
    operable program or batch file.

Similar: Since "if not defined" has checked a variable named "echo" 
(which is "undefined"), it then tries to execute the rest of the 
line: "The-variable-is-not-defined." as a command. This fails and is 
an error. The statement 
should be: if not defined CHECKCONTINUE echo The-variable-is-not-defined. 



## CHECKCONTINUE="" (undefined) and "echo"="something". 
## 73: if defined %CHECKCONTINUE% echo Verify this.

    C:\>if defined echo Verify this.
    An incorrect parameter was
    entered for the command.

Again, similar: Since "if defined" has checked a variable named 
"echo", it then tries to execute the rest of the line starting with 
the word: "Verify" as a command. This happens to be a valid command 
but it also fails because of an incorrect parameter for the command. 
The statement should be: if defined CHECKCONTINUE echo Verify this. 



## CHECKCONTINUE="yes" and the variable named "yes"="" (undefined). 
## 90: if not defined %CHECKCONTINUE% echo CHECKCONTINUE is not defined.

    C:\>if not defined yes echo CHECKCONTINUE is not defined.
    CHECKCONTINUE is not defined.

Here "CHECKCONTINUE" is defined, but "if not defined" still doesn't 
check if the variable named "CHECKCONTINUE" is defined. Since 
CHECKCONTINUE has a value of "yes", "if not defined" is 
checking if a variable named "yes" is defined (which it isn't). 
This causes "if not defined" to proceed and echo the message when 
that's probably not what was intended. The statement 
should be: if not defined CHECKCONTINUE echo CHECKCONTINUE is not defined.

另外,作为的替代方案"set /p",您可以使用"choice"

@echo off
title Registry restore script
rem Restores registry settings and disables the cloud

rem "quotes" around variable name and value for set visibly shows what 
rem the variable is being set to and prevents accidentally including  
rem trailing whitespace in the variable's value.
    set "CHECKCONTINUE="

:listaction
echo I'm about to...
echo 1.) Remove the registry data that specifies settings for TF2
echo 2.) Forcibly disable Steam Cloud.
echo.
choice /c yn /M "Okay to continue"

set "CHECKCONTINUE=%errorlevel%"
if %CHECKCONTINUE% EQU 1 @echo Pressed Y && goto :start
if %CHECKCONTINUE% EQU 2 @echo Pressed N && goto :exit
if %CHECKCONTINUE% EQU 0 @echo Pressed Ctrl-C+n
@echo.

@echo Terminate batch job cancelled. You must enter a reply. Press n to exit.
@echo.
goto :listaction

rem The remainder of your code goes here ...

注意:标签处的代码"loop-notvalid"不是必需的,因为“选择”不接受未定义的响应(y/n)。

此外,从“选择”命令获得“空”响应的唯一方法是,如果用户按下“Ctrl-C”来终止批处理作业,然后在“终止批处理作业(Y/N)?”提示中输入 N(否),表示他们不想退出。上面的代码会捕获该情况并打印一条消息,然后跳转到(转到)“:listaction”标签以重新提示用户,因此您也不需要“loop-noreply”标签处的代码。

无需“重置”错误级别,因为 choice 命令会处理该问题。而且,无需清除变量,因为在检查的值之前,CHECKCONTINUE它始终设置为。%errorlevel%CHECKCONTINUE

默认情况下,choice 是“不区分大小写的”,因此按“Y”或“N”与按“y”或“n”相同。可以通过/cs在 choice 命令行上指定来更改此行为。

相关内容