このバッチ スクリプトが予期せず終了するのはなぜですか?

このバッチ スクリプトが予期せず終了するのはなぜですか?

このバッチ スクリプトは、%CHECKCONTINUE%13 行目 ( ) に何も入力せずに null 値が与えられると終了しますが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

grawity さんの素晴らしい回答を詳しく説明します:

まず、あなたの質問に答えます。%CHECKCONTINUE% に null 値が与えられた場合、バッチ スクリプトが終了するのはなぜですか?

問題は、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"注意: 「choice」は未定義の応答 (y/n) を受け入れないため、label: のコードは必要ありません。

また、「choice」コマンドから「空」の応答を取得する唯一の方法は、ユーザーが「Ctrl-C」を押してバッチ ジョブを終了し、「バッチ ジョブを終了しますか (Y/N)?」プロンプトに N (いいえ) を入力して、終了したくないことを示すことです。上記のコードはこれをキャッチしてメッセージを出力し、「:listaction」ラベルにジャンプ (移動) してユーザーに再度プロンプトを表示するため、「loop-noreply」ラベルのコードも必要ありません。

choice コマンドがそれを処理するので、 errorlevel を「リセット」する必要はありません。また、の値が検査される前にCHECKCONTINUEは常に に等しく設定されているため、変数をクリアする必要もありません。%errorlevel%CHECKCONTINUE

デフォルトでは、choice は「大文字と小文字を区別しない」ため、「Y」または「N」を押すことは、「y」または「n」を押すことと同じです。この動作は、/cschoice コマンドラインで指定することで変更できます。

関連情報