Bagaimana cara menghentikan skrip bash ketika suatu kondisi gagal?

11

Di sini ia menunjukkan untuk menggunakan ||dan &&dalam satu baris untuk menyatukan eksekusi perintah: Bagaimana saya bisa memeriksa kesalahan apt-get dalam skrip bash?

Saya mencoba menghentikan eksekusi skrip jika kondisi tertentu gagal,

misalnya

false || echo "Obvious error because its false on left" && exit

Di sini ia mencetak Obvious error because its false on the leftdan keluar dari konsol yang saya inginkan.

true || echo "This shouldn't print" && exit

Di sini, tidak ada cetak gema tetapi exitperintah juga berjalan, yaitu konsol ditutup, bukankah exitperintah tidak berjalan karena perintah gema di sebelah kanan tidak dijalankan? Atau apakah secara default pernyataan dianggap salah di sisi kiri &&operator?

Sunting: Seharusnya saya sebutkan sebelumnya, tujuan saya adalah untuk mengulang kesalahan dan keluar jika tidak jelas. WRT ke kasus spesifik saya menangkap kesalahan ketika pengelompokan kondisi menggunakan && dan ||, @ bodhi.zazen jawaban memecahkan masalah.

Jawaban @takatakatek membuatnya lebih jelas tentang kontrol aliran dan tautan panduan bash sangat baik

@muru jawaban memiliki penjelasan yang baik tentang mengapa tidak menggunakan set -e jika Anda ingin pesan kesalahan khusus dilemparkan dengan alternatif menggunakan perl dan trap yang saya pikir merupakan cara yang lebih kuat dan lihat sendiri menggunakannya dari skrip bash kedua saya dan seterusnya!

kosol
sumber
Sejauh yang saya tahu itu keluar saat skrip selesai.
Panther

Jawaban:

10

Anda mungkin ingin (seperti yang ditunjukkan oleh steeldriver)

true || { echo "This shouldn't print" && exit; }

Kalau tidak, skrip Anda membaca satu kondisional pada suatu waktu

a atau b dan kemudian keluar

Saya pikir Anda ingin a atau (b dan keluar)?

Harimau kumbang
sumber
4
Sebenarnya, Anda harus menggunakan { ... ; }(seperti yang disarankan @steeldriver), atau yang exithanya keluar dari subkulit, dan shell induk terus menjalankan skrip.
Gordon Davisson
14

Masalahnya adalah bahwa || dan && hanya melewatkan satu bait berikutnya dari rantai perintah ketika kondisinya gagal. Jika Anda menulis struktur blok penuh itu masuk akal. Apa yang Anda tulis menjadi ini:

if ! true ; then
   echo "This shouldn't print"
else
   exit
fi

Yang Anda inginkan adalah ini:

if ! true ; then
   echo "This shouldn't print"
   exit
fi

Ketika Anda menggunakan operator kondisional pintas Bash, lebih baik untuk tidak mencampurnya. Logikanya jauh lebih mudah dimengerti saat Anda menulisnya, dan pembaca Anda akan menghargai gaya Anda yang baik.

takatakatek
sumber
2
Ya menurut saya ini jauh lebih bagaimana pengkondisian dalam bash harus ditulis
derHugo
@takatakatek Ini membuatnya lebih jelas mengapa gagal dalam kasus saya. Saya setuju bahwa logikanya jelas dalam hal ini tetapi bukan alasan untuk mengelompokkan kondisi menggunakan && dan || adalah untuk menghindari mengungkapkannya menggunakan pernyataan kondisional?
kosol
@kosol Ya, && dan || menggabungkan dua hal: Menguji status keluar dari perintah sebelumnya, dan menentukan satu perintah (atau grup perintah) yang akan dieksekusi (untuk &&) atau dilewati (untuk ||). Mereka dapat berguna untuk kasus-kasus yang sangat sederhana, tetapi mereka tidak dapat menggantikan if ... then ... elif ... else ... fiblok yang diekspresikan sepenuhnya . Saya menduga Anda mungkin tidak menyadari bahwa Bash tidak memiliki tipe boolean sejati yang terlihat dalam bahasa yang lebih canggih. Jika status keluar dari perintah adalah 0 (nol), Bash memperlakukan itu sebagai benar / sukses . Jika status keluar tidak nol, Bash memperlakukannya sebagai salah / gagal .
takatakatek
7

Cara termudah untuk gagal pada kesalahan adalah dengan menggunakan -eopsi bash ( set -eatau /bin/bash -edi shebang). Namun, ini tidak membuatnya mudah untuk mengirim pesan kesalahan khusus. Ada beberapa idiom yang mungkin membuatnya lebih sederhana:

die à la Perl

Perl memiliki dieperintah yang sangat mudah untuk mencetak pesan ke stderr dan memunculkan pengecualian, yang biasanya mengarah ke penghentian skrip. Anda dapat meniru ini:

die () {
  ret=$?
  print "%s\n" "$@" >&2
  exit "$ret"
}

false || die "Obvious error because its false on left"
true || die "This shouldn't print"

trap ERR

The trap <command>perintah dapat digunakan untuk menjalankan perintah ketika sinyal yang diterima, atau saat keluar script ( trap ... EXIT), atau ketika perintah mengembalikan kesalahan ( trap ... ERR). Jadi sesuatu seperti:

trap 'ret=$?; printf "%s\n" "$ERR_MSG" >&2; exit "$ret"' ERR

Kemudian:

ERR_MSG="Obvious error because its false here"
false
ERR_MSG="This shouldn't print"
true

Dalam kedua kasus, saya sarankan menggunakan setidaknya exit 1, untuk menunjukkan bahwa skrip gagal . Plain exitakan menggunakan status keluar dari perintah sebelumnya, yang dalam kasus ini akan menjadi echoatau printfperintah, yang biasanya akan berhasil. Menyimpan status keluar dari perintah gagal seperti yang saya lakukan di sini akan menyenangkan.

muru
sumber
6

Anda dapat mencoba sedikit bereksperimen dengan memulai skrip

#!/bin/bash -e

-E memastikan bahwa skrip keluar saat sesuatu mengembalikan false. Kegagalan spesifik kemudian dapat dihindari dengansomething || true

agregat1166877
sumber