이 배치 스크립트가 예기치 않게 종료되는 이유는 무엇입니까?

이 배치 스크립트가 예기치 않게 종료되는 이유는 무엇입니까?

이 배치 스크립트는 %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")는 "치명적이지 않은" 오류입니다. fatal" 오류가 발생하면 오류가 있는 명령문은 실행되지 않고 오류 메시지가 표시되며 배치 스크립트는 다음 명령문부터 계속 실행됩니다.

다음으로, Grarity가 이 라인에 대해 지적했듯이:

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개 라인의 경우 if가 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:의 코드는 필요하지 않습니다.

또한 "선택" 명령에서 "빈" 응답을 얻는 유일한 방법은 사용자가 "Ctrl-C"를 눌러 일괄 작업을 종료한 다음 "일괄 작업 종료(Y/ N)?" 프롬프트가 표시되어 종료를 원하지 않음을 나타냅니다. 위의 코드는 이를 포착하고 메시지를 인쇄한 다음 ":listaction" 레이블로 점프(goto)하여 사용자에게 다시 메시지를 표시하므로 "loop-noreply" 레이블에 있는 코드도 필요하지 않습니다.

choice 명령이 이를 처리하므로 errorlevel을 "재설정"할 필요가 없습니다. 그리고 변수의 값을 검사하기 전에는 CHECKCONTINUE항상 와 동일하게 설정되므로 변수를 지울 필요가 없습니다 .%errorlevel%CHECKCONTINUE

기본적으로 선택은 "대소문자를 구분하지 않습니다". 따라서 "Y" 또는 "N"을 누르는 것은 "y" 또는 "n"을 누르는 것과 같습니다. 이 동작은 /cs선택 명령줄에서 지정하여 변경할 수 있습니다 .

관련 정보