Mengapa skrip batch ini berhenti secara tak terduga?


2

Script batch ini berakhir ketika %CHECKCONTINUE% diberi nilai nol dengan tidak memasukkan apa pun pada baris 13 ( SET /p CHECKCONTINUE=Okay to continue? (y/n): ), kenapa ini?

@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

Ada apa dengan orang yang menulis skrip batch dalam huruf besar semua? (Pertanyaan gaya pengkodean, saya tahu, tapi ...)
grawity

1
Kebiasaan menulis skrip shell.
Matthieu Cartier

Jawaban:


3

Pertama, pada banyak baris yang Anda miliki:

SET %CHECKCONTINUE%=

Ini tidak berubah CHECKCONTINUE tetapi menggunakan nya nilai sebagai nama variabel.

Ubah ke:

SET CHECKCONTINUE=

Ini juga akan lebih baik jika Anda memindahkannya tepat di atas set /p ... - dengan cara ini Anda hanya perlu sekali.


if defined juga hanya membutuhkan nama variabel, jadi alih-alih

IF DEFINED %CHECKCONTINUE% GOTO loop-notvalid

kamu harus menggunakan:

IF DEFINED CHECKCONTINUE GOTO loop-notvalid

Hal yang sama berlaku untuk baris ini:

IF NOT DEFINED %CHECKCONTINUE% GOTO loop-noreply

Namun, dapat disingkat menjadi:

GOTO loop-noreply

Jika variabel didefinisikan, eksekusi tidak akan pernah mencapai garis ini ( if defined ... atas)


Beginilah cara saya menulisnya:

@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

Jika skrip dijalankan dari dalam shell Prompt Perintah interaktif, sebuah dataran exit akan mengakhiri shell juga, bukan hanya skrip. Dalam sebuah skrip, seseorang harus menggunakan keduanya goto :EOF atau exit /b. ( endlocal tidak perlu.)
grawity

Itu masih berakhir jika Anda menekan enter tanpa input aktif set /p check=Okay to continue? (y/n) :(
Matthieu Cartier

@neurolysis: Jika Anda berbicara tentang versi yang ditulis ulang dari posting saya, itu karena saya menulisnya. IMO, karena ada opsi default yang masuk akal ("tidak, jangan pulihkan"), menekan Enter seharusnya hanya memilih opsi itu. Jika Anda tidak setuju, ubah ke if not defined check goto :loop-noreply.
grawity

Nah, itulah masalahnya. Bahkan dengan if not defined check goto :loop-noreply sebagai cek, itu berakhir ...
Matthieu Cartier

@neurolysis: ... apakah Anda menambahkan (a) :loop-noreply bagian? Juga, coba pindahkan not defined periksa segera setelah itu set /p. Dan jalankan semuanya dari dalam jendela Command Prompt, pesan kesalahan adalah bermanfaat.
grawity

1

Memperluas jawaban hebat gravitasi:

Pertama, untuk menjawab pertanyaan Anda: Mengapa skrip batch diakhiri ketika% CHECKCONTINUE% diberi nilai nol?

Masalahnya adalah, pada baris 16, Anda melakukan ini:

if %CHECKCONTINUE%==n GOTO exit

Sejak CHECKCONTINUE adalah "undefined", ia mengevaluasi ke string "kosong", sehingga pernyataan pada baris 16 benar-benar melakukan:

if ==n GOTO exit

Pernyataan ini tidak valid karena tidak ada apa pun di sisi kiri "==". Jadi, skrip kumpulan berakhir ketika mencoba menjalankan pernyataan yang diformat secara tidak benar:

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

Anda akan memiliki masalah yang sama jika seseorang mengetik sesuatu yang berisi spasi di dalamnya:

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

Untuk memperbaiki ini, Anda harus menggunakan tanda kutip ganda di sekitar istilah seperti ini:

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

Ini diperlukan jika variabel yang digunakan bisa "kosong" atau jika mereka bisa menyematkan spasi putih, tapi itu ide yang baik untuk selalu menggunakan tanda kutip ganda ketika mengevaluasi dengan "==".

Catatan: Beberapa kesalahan (seperti yang di atas dengan "if" dan "==" adalah kesalahan "fatal" yang akan menyebabkan eksekusi skrip batch untuk segera berhenti. Kesalahan lainnya (seperti yang di bawah ini dengan "set" ), adalah kesalahan "tidak fatal". Untuk kesalahan "non-fatal", pernyataan dengan kesalahan TIDAK dieksekusi, pesan kesalahan ditampilkan, dan skrip batch terus berjalan dimulai dengan pernyataan berikutnya

Berikutnya, seperti yang ditunjukkan oleh grawity tentang baris ini:

set %CHECKCONTINUE%=

Ini tidak mengubah CHECKCONTINUE tetapi menggunakan nilainya sebagai nama variabel.

Sekali lagi, jika CHECKCONTINUE "tidak terdefinisi", itu akan mengevaluasi ke string "kosong", jadi pernyataan itu benar-benar berfungsi:

set =

Ini juga pernyataan yang tidak valid karena tidak ada apa pun di sisi kiri "=".

Dan garis-garis ini:

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

"if defined" (dan "if not defined" ) mengharapkan nama variabel, bukan nilai variabel. Jika CHECKCONTINUE tidak terdefinisi, %CHECKCONTINUE% akan dievaluasi menjadi string kosong, dan pernyataan ini sebenarnya adalah:

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

Sini, "if defined" (dan "if not defined" ) akan memeriksa apakah variabel bernama GOTO didefinisikan atau tidak.

Juga, untuk 3 baris ini, jika CHECKCONTINUE sebenarnya didefinisikan, "set" dan "if defined" akan beroperasi pada "value" variabel, bukan "name" dari variabel itu sendiri. Jadi jika CHECKCONTINUE sudah memiliki nilai "y", kemudian:

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

sebenarnya akan dilihat sebagai:

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

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

Menjalankan "script.cmd" Anda akan mendapatkan:

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

Juga, sebagai alternatif "set /p", kamu bisa menggunakan "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 ...

Catatan: kode pada label: "loop-notvalid" tidak diperlukan karena "pilihan" tidak akan menerima tanggapan yang tidak ditentukan (y / n).

Juga, satu-satunya cara untuk mendapatkan respons "kosong" dari commnad "pilihan", adalah jika pengguna menekan "Ctrl-C" untuk mengakhiri pekerjaan batch, dan kemudian memasukkan N (Tidak) ke "Hentikan pekerjaan batch (Y / N)? " cepat, menunjukkan mereka TIDAK ingin keluar. Kode di atas menangkap itu dan mencetak pesan kemudian melompat (kebagian) ke label ": listaction" untuk meminta kembali kepada pengguna, jadi Anda tidak perlu kode pada label "loop-noreply" juga.

Tidak perlu "mengatur ulang" tingkat kesalahan karena perintah pilihan menangani itu. Dan, tidak perlu menghapusnya CHECKCONTINUE variabel karena selalu disetel sama dengan %errorlevel% sebelum nilai CHECKCONTINUE diperiksa.

Secara default, pilihan adalah "case-insensitive", jadi menekan "Y" atau "N" sama dengan menekan "y" atau "n". Perilaku ini dapat diubah dengan menetapkan /cs pada baris perintah pilihan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.