Saya memiliki skrip shell yang menjalankan sejumlah perintah. Bagaimana cara saya membuat skrip shell keluar jika ada perintah yang keluar dengan kode keluar non-nol?
Saya menjawab dengan asumsi Anda menggunakan bash, tetapi jika shell yang sangat berbeda dapat Anda tentukan dalam posting Anda?
Martin W
Metode keras: uji nilai $?setelah setiap perintah. Metode mudah: letakkan set -eatau #!/bin/bash -edi bagian atas skrip Bash Anda.
mwfearnley
Jawaban:
494
Setelah setiap perintah, kode keluar dapat ditemukan dalam $?variabel sehingga Anda akan memiliki sesuatu seperti:
ls -al file.ext
rc=$?;if[[ $rc !=0]];then exit $rc;fi
Anda harus berhati-hati dengan perintah pipa karena $?hanya memberi Anda kode kembali elemen terakhir dalam pipa, jadi dalam kode:
ls -al file.ext | sed 's/^/xx: /"
tidak akan mengembalikan kode kesalahan jika file tidak ada (karena sedbagian dari pipa sebenarnya bekerja, mengembalikan 0).
The bashshell benar-benar menyediakan sebuah array yang dapat membantu dalam kasus itu, bahwa menjadi PIPESTATUS. Array ini memiliki satu elemen untuk setiap komponen pipa, yang dapat Anda akses secara individual seperti ${PIPESTATUS[0]}:
pax> false | true ; echo ${PIPESTATUS[0]}1
Perhatikan bahwa ini memberi Anda hasil dari falseperintah, bukan seluruh pipa. Anda juga bisa mendapatkan seluruh daftar untuk diproses sesuai keinginan Anda:
Fitur yang sama hanya dalam satu baris kode portabel: ls -al file.ext || exit $?([[]] tidak portabel)
MarcH
19
MarcH, saya pikir Anda akan menemukan bahwa [[ ]]ini cukup portabel bash, yang merupakan pertanyaan yang ditandai :-) Anehnya, lstidak berfungsi command.comsehingga tidak portabel juga, saya tahu, tapi itu jenis argumen yang sama dengan Anda menyajikan.
paxdiablo
39
Saya tahu ini kuno, tetapi harus dicatat bahwa Anda bisa mendapatkan kode keluar dari perintah dalam pipa melalui array PIPESTATUS(yaitu, ${PIPESTATUS[0]}untuk perintah pertama, ${PIPESTATUS[1]}untuk yang kedua, atau ${PIPESTATUS[*]}untuk daftar semua stati keluar.
DevSolar
11
Perlu ditekankan bahwa scripting shell elegan dan idiomatik sangat jarang perlu diperiksa $?secara langsung. Anda biasanya menginginkan sesuatu seperti if ls -al file.ext; then : nothing; else exit $?; fiyang tentu saja seperti @MarcH katakan setara dengan ls -al file.ext || exit $?tetapi jika thenatau elseklausa yang agak lebih kompleks, itu lebih dapat dipertahankan.
tripleee
9
[[ $rc != 0 ]]akan memberikan 0: not foundatau 1: not foundkesalahan. Ini harus diubah menjadi [ $rc -ne 0 ]. Juga rc=$?bisa dihapus dan digunakan [ $? -ne 0 ].
CurtisLeeBolin
223
Jika Anda ingin bekerja dengan $ ?, Anda harus memeriksanya setelah setiap perintah, karena $? diperbarui setelah setiap perintah keluar. Ini berarti bahwa jika Anda menjalankan pipa, Anda hanya akan mendapatkan kode keluar dari proses terakhir dalam pipa.
Pendekatan lain adalah melakukan ini:
set-e
set-o pipefail
Jika Anda meletakkan ini di bagian atas skrip shell, sepertinya bash akan menangani ini untuk Anda. Seperti yang dicatat oleh poster sebelumnya, "set -e" akan menyebabkan bash keluar dengan kesalahan pada perintah sederhana apa pun. "set -o pipefail" akan menyebabkan bash keluar dengan kesalahan pada perintah apa pun dalam pipa juga.
Lihat di sini atau di sini untuk sedikit lebih banyak diskusi tentang masalah ini. Berikut ini adalah bagian manual bash pada set builtin.
Ini benar-benar harus menjadi jawaban teratas: jauh lebih mudah untuk melakukan ini daripada menggunakan PIPESTATUSdan memeriksa kode keluar di mana-mana.
candu
2
#!/bin/bash -eadalah satu-satunya cara untuk memulai skrip shell. Anda selalu dapat menggunakan hal-hal seperti foo || handle_error $?jika Anda harus benar-benar memeriksa status keluar.
Davis Herring
53
" set -e" mungkin cara termudah untuk melakukan ini. Masukkan saja sebelum perintah apa pun di program Anda.
@SwaroopCH set -eskrip Anda akan dibatalkan jika ada perintah dalam skrip Anda keluar dengan status kesalahan dan Anda tidak menangani kesalahan ini.
Andrew
2
set -e100% setara dengan set -o errexityang tidak seperti yang sebelumnya dapat dicari. Cari opengroup + errexit untuk dokumentasi resmi.
MarcH
30
Jika Anda memanggil keluar di bash tanpa parameter, itu akan mengembalikan kode keluar dari perintah terakhir. Dikombinasikan dengan ATAU bash seharusnya hanya memanggil keluar, jika perintah sebelumnya gagal. Tapi saya belum menguji ini.
command1 || keluar;
command2 || keluar;
Bash juga akan menyimpan kode keluar dari perintah terakhir di variabel $ ?.
Bagaimana cara mendapatkan kode keluar cmd1masukcmd1|cmd2
Pertama, perhatikan bahwa cmd1kode keluar bisa berupa nol dan masih tidak berarti kesalahan. Ini terjadi misalnya di
cmd | head -1
Anda mungkin mengamati status keluar 141 (atau 269 dengan ksh93) cmd1, tetapi karena cmdterputus oleh sinyal SIGPIPE ketika
head -1dihentikan setelah membaca satu baris.
Untuk mengetahui status keluar dari elemen-elemen pipa
cmd1 | cmd2 | cmd3
Sebuah. dengan zsh:
Kode keluar disediakan dalam larik khusus pipestatus.
cmd1kode keluar $pipestatus[1], cmd3kode keluar
$pipestatus[3], sehingga $?selalu sama dengan
$pipestatus[-1].
b. dengan bash:
Kode keluar disediakan dalam PIPESTATUSlarik khusus.
cmd1kode keluar ${PIPESTATUS[0]}, cmd3kode keluar
${PIPESTATUS[2]}, sehingga $?selalu sama dengan
${PIPESTATUS: -1}.
# this will trap any errors or commands with non-zero exit status# by calling function catch_errors()
trap catch_errors ERR;## ... the rest of the script goes here# function catch_errors(){# do whatever on errors# #
echo "script aborted, because of errors";
exit 0;}
+1 Ini adalah solusi paling sederhana yang saya cari. Selain itu, Anda juga dapat menulis if (! command)jika Anda mengharapkan kode kesalahan bukan nol dari perintah.
Berci
ini untuk perintah sekuensial .. bagaimana jika saya ingin meluncurkan 3 itu secara paralel dan membunuh semua orang jika ada yang gagal?
Vasile Surdu
4
##------------------------------------------------------------------------------# run a command on failure exit with message# doPrintHelp: doRunCmdOrExit "$cmd"# call by:# set -e ; doRunCmdOrExit "$cmd" ; set +e#------------------------------------------------------------------------------
doRunCmdOrExit(){
cmd="$@";
doLog "DEBUG running cmd or exit: \"$cmd\""
msg=$($cmd 2>&1)
export exit_code=$?# if occured during the execution exit with error
error_msg="Failed to run the command:
\"$cmd\" with the output:
\"$msg\" !!!"if[ $exit_code -ne 0];then
doLog "ERROR $msg"
doLog "FATAL $msg"
doExit "$exit_code""$error_msg"else#if no errors occured just log the message
doLog "DEBUG : cmdoutput : \"$msg\""
doLog "INFO $msg"fi}#eof func doRunCmdOrExit
$?
setelah setiap perintah. Metode mudah: letakkanset -e
atau#!/bin/bash -e
di bagian atas skrip Bash Anda.Jawaban:
Setelah setiap perintah, kode keluar dapat ditemukan dalam
$?
variabel sehingga Anda akan memiliki sesuatu seperti:Anda harus berhati-hati dengan perintah pipa karena
$?
hanya memberi Anda kode kembali elemen terakhir dalam pipa, jadi dalam kode:tidak akan mengembalikan kode kesalahan jika file tidak ada (karena
sed
bagian dari pipa sebenarnya bekerja, mengembalikan 0).The
bash
shell benar-benar menyediakan sebuah array yang dapat membantu dalam kasus itu, bahwa menjadiPIPESTATUS
. Array ini memiliki satu elemen untuk setiap komponen pipa, yang dapat Anda akses secara individual seperti${PIPESTATUS[0]}
:Perhatikan bahwa ini memberi Anda hasil dari
false
perintah, bukan seluruh pipa. Anda juga bisa mendapatkan seluruh daftar untuk diproses sesuai keinginan Anda:Jika Anda ingin mendapatkan kode kesalahan terbesar dari sebuah pipa, Anda bisa menggunakan sesuatu seperti:
Ini berjalan melalui masing-masing
PIPESTATUS
elemen pada gilirannya, menyimpannyarc
jika itu lebih besar dari nilai sebelumnyarc
.sumber
ls -al file.ext || exit $?
([[]] tidak portabel)[[ ]]
ini cukup portabelbash
, yang merupakan pertanyaan yang ditandai :-) Anehnya,ls
tidak berfungsicommand.com
sehingga tidak portabel juga, saya tahu, tapi itu jenis argumen yang sama dengan Anda menyajikan.PIPESTATUS
(yaitu,${PIPESTATUS[0]}
untuk perintah pertama,${PIPESTATUS[1]}
untuk yang kedua, atau${PIPESTATUS[*]}
untuk daftar semua stati keluar.$?
secara langsung. Anda biasanya menginginkan sesuatu sepertiif ls -al file.ext; then : nothing; else exit $?; fi
yang tentu saja seperti @MarcH katakan setara denganls -al file.ext || exit $?
tetapi jikathen
atauelse
klausa yang agak lebih kompleks, itu lebih dapat dipertahankan.[[ $rc != 0 ]]
akan memberikan0: not found
atau1: not found
kesalahan. Ini harus diubah menjadi[ $rc -ne 0 ]
. Jugarc=$?
bisa dihapus dan digunakan[ $? -ne 0 ]
.Jika Anda ingin bekerja dengan $ ?, Anda harus memeriksanya setelah setiap perintah, karena $? diperbarui setelah setiap perintah keluar. Ini berarti bahwa jika Anda menjalankan pipa, Anda hanya akan mendapatkan kode keluar dari proses terakhir dalam pipa.
Pendekatan lain adalah melakukan ini:
Jika Anda meletakkan ini di bagian atas skrip shell, sepertinya bash akan menangani ini untuk Anda. Seperti yang dicatat oleh poster sebelumnya, "set -e" akan menyebabkan bash keluar dengan kesalahan pada perintah sederhana apa pun. "set -o pipefail" akan menyebabkan bash keluar dengan kesalahan pada perintah apa pun dalam pipa juga.
Lihat di sini atau di sini untuk sedikit lebih banyak diskusi tentang masalah ini. Berikut ini adalah bagian manual bash pada set builtin.
sumber
PIPESTATUS
dan memeriksa kode keluar di mana-mana.#!/bin/bash -e
adalah satu-satunya cara untuk memulai skrip shell. Anda selalu dapat menggunakan hal-hal sepertifoo || handle_error $?
jika Anda harus benar-benar memeriksa status keluar."
set -e
" mungkin cara termudah untuk melakukan ini. Masukkan saja sebelum perintah apa pun di program Anda.sumber
set -e
skrip Anda akan dibatalkan jika ada perintah dalam skrip Anda keluar dengan status kesalahan dan Anda tidak menangani kesalahan ini.set -e
100% setara denganset -o errexit
yang tidak seperti yang sebelumnya dapat dicari. Cari opengroup + errexit untuk dokumentasi resmi.Jika Anda memanggil keluar di bash tanpa parameter, itu akan mengembalikan kode keluar dari perintah terakhir. Dikombinasikan dengan ATAU bash seharusnya hanya memanggil keluar, jika perintah sebelumnya gagal. Tapi saya belum menguji ini.
Bash juga akan menyimpan kode keluar dari perintah terakhir di variabel $ ?.
sumber
sumber
exit $?
(tanpa parens)?http://cfaj.freeshell.org/shell/cus-faq-2.html#11
Bagaimana cara mendapatkan kode keluar
cmd1
masukcmd1|cmd2
Pertama, perhatikan bahwa
cmd1
kode keluar bisa berupa nol dan masih tidak berarti kesalahan. Ini terjadi misalnya diAnda mungkin mengamati status keluar 141 (atau 269 dengan ksh93)
cmd1
, tetapi karenacmd
terputus oleh sinyal SIGPIPE ketikahead -1
dihentikan setelah membaca satu baris.Untuk mengetahui status keluar dari elemen-elemen pipa
cmd1 | cmd2 | cmd3
Sebuah. dengan zsh:
Kode keluar disediakan dalam larik khusus pipestatus.
cmd1
kode keluar$pipestatus[1]
,cmd3
kode keluar$pipestatus[3]
, sehingga$?
selalu sama dengan$pipestatus[-1]
.b. dengan bash:
Kode keluar disediakan dalam
PIPESTATUS
larik khusus.cmd1
kode keluar${PIPESTATUS[0]}
,cmd3
kode keluar${PIPESTATUS[2]}
, sehingga$?
selalu sama dengan${PIPESTATUS: -1}
....
Untuk lebih jelasnya lihat tautan berikut .
sumber
untuk bash:
sumber
Dalam bash ini mudah, cukup ikat mereka dengan &&:
Anda juga dapat menggunakan nested jika konstruk:
sumber
if (! command)
jika Anda mengharapkan kode kesalahan bukan nol dari perintah.sumber
$*
; gunakan"$@"
sebagai gantinya untuk menghemat ruang dan wildcard.