Perché questo script batch termina in modo imprevisto?


2

Questo script batch termina quando %CHECKCONTINUE% viene assegnato un valore nullo non immettendo nulla sulla riga 13 ( SET /p CHECKCONTINUE=Okay to continue? (y/n): ), perchè è questo?

@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

Che cosa succede alle persone che scrivono script in blocco in maiuscolo? (Domanda sullo stile di codifica, lo so, ma ...)
grawity

1
Abituati a scrivere script di shell.
Matthieu Cartier

Risposte:


3

Innanzitutto, su più righe hai:

SET %CHECKCONTINUE%=

Questo non modifica CHECKCONTINUE ma usa il suo valore come il nome della variabile.

Cambiarlo in:

SET CHECKCONTINUE=

Sarebbe anche meglio se lo spostassi immediatamente sopra set /p ... - In questo modo ne avresti solo bisogno una volta.


if defined accetta anche solo un nome variabile, quindi invece di

IF DEFINED %CHECKCONTINUE% GOTO loop-notvalid

dovresti usare:

IF DEFINED CHECKCONTINUE GOTO loop-notvalid

Lo stesso vale per questa linea:

IF NOT DEFINED %CHECKCONTINUE% GOTO loop-noreply

Tuttavia, può essere abbreviato in:

GOTO loop-noreply

Se la variabile fosse stata definita, l'esecuzione non avrebbe mai raggiunto comunque questa linea ( if defined ... sopra)


Questo è come l'avrei scritto:

@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

Se lo script è stato eseguito da all'interno di una shell del prompt dei comandi interattiva, una semplice exit finirebbe anche la shell, non solo la sceneggiatura. All'interno di uno script, si dovrebbe usare entrambi goto :EOF o exit /b. ( endlocal non è necessario.)
grawity

Termina comunque se si preme Invio senza input set /p check=Okay to continue? (y/n) :(
Matthieu Cartier

@neurolysis: se stai parlando della versione riscritta dal mio post, è perché l'ho scritta. IMO, dal momento che esiste un'opzione predefinita sensata ("no, non ripristinare"), premendo Invio dovrebbe semplicemente scegliere quell'opzione. Se non sei d'accordo, cambia in if not defined check goto :loop-noreply.
grawity

Bene, questo è il problema a portata di mano. Anche con if not defined check goto :loop-noreply come controllo, termina ...
Matthieu Cartier

@neurolysis: ... hai (ri) aggiunto un :loop-noreply sezione? Inoltre, prova a spostare il not defined controllare subito dopo set /p. E lanciare tutto da una finestra del prompt dei comandi, messaggi di errore siamo utile.
grawity

1

Espansione della grande risposta della gravità:

In primo luogo, per rispondere alla tua domanda: Perché lo script batch termina quando% CHECKCONTINUE% ha un valore nullo?

Il problema è che, nella riga 16, fai questo:

if %CHECKCONTINUE%==n GOTO exit

Da CHECKCONTINUE è "indefinito", valuta una stringa "vuota", quindi l'affermazione sulla linea 16 sta effettivamente facendo:

if ==n GOTO exit

Questa è una dichiarazione non valida perché non c'è nulla sul lato sinistro del "==". Pertanto, lo script batch termina quando tenta di eseguire un'istruzione formattata in modo errato:

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:\>

Avresti un problema simile se qualcuno digita qualcosa che contiene uno spazio al suo interno:

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:\>

Per risolvere questo problema, dovresti usare virgolette doppie per i termini come questo:

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

Questo è necessario se le variabili utilizzate potrebbero essere "vuote" o se potrebbero avere spazi bianchi incorporati, ma è solo una buona idea usare sempre le virgolette quando si valuta con "==".

Nota: alcuni errori (come quello sopra con "if" e "==" sono errori "fatali" che causano l'arresto immediato dello script batch. Altri errori (come quello sotto con "set" ), sono errori "non fatali". Per errori "non fatali", l'istruzione con l'errore NON viene eseguita, viene visualizzato un messaggio di errore e lo script batch continua a essere eseguito a partire dall'istruzione successiva

Quindi, come ha sottolineato Grawity su questa linea:

set %CHECKCONTINUE%=

Questo non modifica CHECKCONTINUE ma usa il suo valore come nome della variabile.

Di nuovo, se CHECKCONTINUE era "indefinito", valuterebbe una stringa "vuota", quindi l'affermazione sta effettivamente facendo:

set =

Questa è anche una dichiarazione non valida perché non c'è nulla sul lato sinistro del "=".

E queste righe:

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

"if defined" (e "if not defined" ) si aspetta un nome di variabile, non il valore di una variabile. Se CHECKCONTINUE era indefinito, %CHECKCONTINUE% valuterà una stringa vuota e queste dichiarazioni sarebbero in realtà:

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

Qui, "if defined" (e "if not defined" ) sta andando a verificare se una variabile di nome GOTO è definito o no.

Inoltre, per queste 3 linee, se CHECKCONTINUE è stato effettivamente definito, "set" e "if defined" opererebbe sul "value" della variabile, piuttosto che il "name" della variabile stessa. Quindi se CHECKCONTINUE aveva già un valore di "y", poi:

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

sarebbe effettivamente visto come:

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

Esempio "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.

Eseguendo "script.cmd" otterresti:

## 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.

Inoltre, in alternativa a "set /p", potresti usare "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 ...

Nota: il codice sull'etichetta: "loop-notvalid" non è necessario perché "choice" non accetta risposte non definite (y / n).

Inoltre, l'unico modo per ottenere una risposta "vuota" dal comando "choice" è se l'utente preme "Ctrl-C" per terminare il lavoro batch, quindi inserisce N (No) in "Termina processo batch" (Y / N)?" prompt, indicando che NON vogliono uscire. Il codice sopra lo cattura e stampa un messaggio, quindi salta (goto) all'etichetta ": listaction" per richiedere nuovamente all'utente, quindi non è necessario il codice nell'etichetta "loop-noreply".

Non è necessario "resettare" errorlevel poiché il comando choice si occupa di ciò. E, non è necessario cancellare il CHECKCONTINUE variabile perché è sempre impostata uguale a %errorlevel% prima del valore di CHECKCONTINUE viene esaminato

Per impostazione predefinita, la scelta è "maiuscole e minuscole", quindi premere "Y" o "N" equivale a premere "y" o "n". Questo comportamento può essere modificato specificando /cs sulla riga di comando scelta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.