Bagaimana melakukan sesuatu secara kondisional jika suatu perintah berhasil atau gagal

283

Bagaimana saya bisa melakukan sesuatu seperti ini di bash?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi
Michael Mrozek
sumber

Jawaban:

359

Bagaimana melakukan sesuatu secara kondisional jika suatu perintah berhasil atau gagal

Itulah yang dilakukan ifpernyataan bash :

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Menambahkan informasi dari komentar: Anda tidak perlu menggunakan [... ]sintaks dalam kasus ini. [itu sendiri perintah, sangat hampir setara dengan test. Ini mungkin perintah yang paling umum digunakan dalam if, yang dapat mengarah pada asumsi bahwa itu adalah bagian dari sintaks shell. Tetapi jika Anda ingin menguji apakah suatu perintah berhasil atau tidak, gunakan perintah itu sendiri secara langsung if, seperti yang ditunjukkan di atas.

Sunting: Mengutip pertanyaan di bagian atas untuk kejelasan (jawaban ini tidak muncul di dekat bagian atas halaman).

Keith Thompson
sumber
11
Perhatikan bahwa titik koma itu penting.
Thorbjørn Ravn Andersen
21
Atau Anda bisa memakai thenjalur terpisah.
l0b0
36
@ Jo: Saya pikir maksud Anda if ! command ; then ... ; fi. [itu sendiri perintah, dan itu tidak diperlukan dalam kasus ini.
Keith Thompson
20
@ Jo: Cara saya juga memiliki sifat benar. if [ ! command ]tidak mengeksekusi command; itu memperlakukan commandsebagai string dan memperlakukannya sebagai benar karena memiliki panjang yang tidak nol. [adalah sinonim untuk testperintah
Keith Thompson
5
Ups. Kamu benar.
Joe
133

Untuk hal-hal kecil yang Anda inginkan terjadi jika perintah shell berfungsi, Anda bisa menggunakan &&konstruk:

rm -rf somedir && trace_output "Removed the directory"

Demikian pula untuk hal-hal kecil yang Anda inginkan terjadi ketika perintah shell gagal, Anda dapat menggunakan ||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

Atau keduanya

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Mungkin tidak bijaksana untuk melakukan banyak hal dengan konstruksi ini, tetapi mereka kadang-kadang dapat membuat aliran kontrol lebih jelas.

Bruce Ediger
sumber
3
Mereka lebih pendek dan (setidaknya dalam beberapa cangkang) lebih cepat. Saya ngeri mengingat skrip instalasi monster Ultrix yang ditulis hanya dengan konstruksi kondisional ini yang pernah saya coba
pecahkan
101

Periksa nilai $?, yang berisi hasil dari mengeksekusi perintah / fungsi terbaru:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

sumber
11
Meskipun secara teknis benar (dan dengan demikian tidak menjamin downvote), itu tidak menggunakan ifidiom Bash . Saya lebih suka jawaban Keith Thompson.
janmoesen
16
Ada manfaat untuk idiom ini - mempertahankan nilai pengembalian. Secara keseluruhan, saya menemukan ini lebih kuat, meskipun lebih bertele-tele. ini juga lebih mudah dibaca.
taxilian
2
Apa itu "Bash's if idiom"?
Sekarang,
3
@Nowaker Fakta bahwa satu-satunya tujuan ifadalah untuk melakukan ini. Kondisi kontrol aliran di Bash semuanya memeriksa di $?belakang layar; itulah yang mereka lakukan. Memeriksa nilainya secara eksplisit seharusnya tidak perlu dalam sebagian besar kasus, dan biasanya merupakan antipattern pemula.
tripleee
1
@lunakid Jika Anda tidak peduli dengan kode keluar, if ! cmdtidak apa-apa. Kalau tidak, pengaturan umum adalah menggunakan elseklausa. Anda tidak dapat memiliki yang kosong thentetapi terkadang Anda melihat di then : nothing; elsemana :no-op signifikan. trueakan bekerja di sana juga.
tripleee
47

Ini bekerja untuk saya:

command && echo "OK" || echo "NOK"

jika commandberhasil, maka echo "OK"dieksekusi, dan karena berhasil, eksekusi berhenti di sana. Kalau tidak, &&akan dilewati, dan echo "NOK"dieksekusi.

Rumm Jerman
sumber
5
Jika Anda ingin melakukan sesuatu jika gagal, dan mempertahankan kode keluar (untuk ditampilkan di command prompt atau tes dalam skrip), Anda dapat melakukan ini: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Sam Hasler
2
@ Sam-Hasler: bukankah seharusnya begitu command && echo "OK" || (c=$?; echo "NOK"; (exit $c))?
jrw32982
9
Juga, jika echo "OK"bagian itu sendiri bisa gagal, maka ini lebih baik:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982
@ jrw32982, Bagus, saya telah menggunakan konstruksi sebelumnya, tetapi tidak yang terakhir.
Sam Hasler
Jawaban sebenarnya ada di komentar, terima kasih semuanya!
Joshua Pinter
6

Perlu dicatat bahwa if...then...fidan &&/ ||jenis pendekatan berkaitan dengan status keluar yang dikembalikan oleh perintah yang ingin kami uji (0 saat berhasil); namun, beberapa perintah tidak mengembalikan status keluar yang tidak nol jika perintah gagal atau tidak dapat menangani input. Ini berarti bahwa pendekatan biasa ifdan &&/ ||tidak akan bekerja untuk perintah-perintah tertentu.

Misalnya, di Linux, GNU filemasih keluar dengan 0 jika menerima file yang tidak ada sebagai argumen dan findtidak dapat menemukan file yang ditentukan pengguna.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

Dalam kasus seperti itu, salah satu cara potensial kita dapat menangani situasi adalah dengan membaca stderr/ stdinpesan, misalnya yang dikembalikan dengan fileperintah, atau parse output dari perintah seperti di find. Untuk tujuan itu, casepernyataan dapat digunakan.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found
Sergiy Kolodyazhnyy
sumber
2

Rawan kesalahan paling banyak yang bisa saya buat adalah:

  • Pertama, dapatkan nilainya. Misalkan Anda melakukan sesuatu seperti:

RR=$?

Sekarang, tidak hanya untuk situasi ini, tetapi orang lain yang mungkin Anda hadapi, pertimbangkan:

variabel yang ditentukan:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Jawab: ya

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Jawab: tidak

variabel tidak terdefinisi:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Jawab: tidak

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Jawab: ya

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Jawab: ya

Ini mencegah semua jenis kesalahan.

Anda mungkin mengetahui semua sintaks, tetapi di sini beberapa tips:

  • Gunakan tanda kutip. Hindari "blank"menjadi nothing.
  • Notasi modern baru untuk variabel adalah ${variable}.
  • Menambahkan nol yang digabungkan sebelum nomor Anda juga menghindari "no number at all".
  • Tapi tunggu, menambahkan nol membuat angka menjadi base-8. Anda akan mendapatkan kesalahan seperti:
    • value too great for base (error token is "08")untuk nomor di atas 7. Saat itulah yang 10#berperan:
    • 10#memaksa nomor menjadi base-10.
Dr Beco
sumber
1

Kamu bisa melakukan ini:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi
Daniel
sumber
9
Itu bekerja tetapi berbelit-belit. Anda menjalankan ping dalam subkulit dari sebuah subkulit, output dari pingditangkap karena menjalankannya sebagai perintah. Tetapi karena output diarahkan ke / dev / null yang akan selalu menjadi string kosong. Jadi Anda tidak menjalankan apa pun dalam subkulit, yang berarti status keluar sebelumnya (dari subkunci substitusi perintah, yaitu ping) akan dipertahankan. Jelas, cara yang benar ada di if ping ...; thensini.
Stéphane Chazelas
1

Seperti disebutkan di bagian lain di utas ini, pertanyaan aslinya pada dasarnya menjawab sendiri. Berikut ini adalah ilustrasi yang menunjukkan bahwa ifkondisi mungkin juga bersarang.

Contoh ini digunakan ifuntuk memeriksa apakah ada file dan apakah itu file biasa. Jika kondisi tersebut benar, periksa apakah ukurannya lebih besar dari 0.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi
kuarsainquartz
sumber
2
Itulah yang dilakukan jawaban Anda, tetapi jawaban Anda tidak menjawab pertanyaan itu.
Jeff Schaller
@ JeffSchaller, terima kasih atas catatan Anda. Saya mengedit posting saya untuk memasukkan referensi ke pertanyaan.
quartzinquartz
1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi
Nick Vladiceanu
sumber
3
Ini terlihat seperti jawaban yang diterima ; tolong cantumkan pekerjaan yang Anda gunakan kembali, atau tambahkan upaya Anda sendiri ke dalam jawaban Anda.
Jeff Schaller
-3

Ini bisa dilakukan hanya dengan cara ini karena $?memberi Anda status perintah terakhir yang dijalankan.

Jadi bisa jadi

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi
vipin mishra
sumber
1
Downvote: Ini hanya memparafrasekan jawaban sebelumnya yang berhak menerima kritik karena tidak aktif.
tripleee