Apakah pernyataan lain setara dengan logika dan && atau || dan di mana saya harus memilih satu dari yang lain?

27

Saya belajar tentang struktur pengambilan keputusan dan saya menemukan kode-kode ini:

if [ -f ./myfile ]
then
     cat ./myfile
else
     cat /home/user/myfile
fi


[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile

Keduanya berperilaku sama. Apakah ada keuntungan menggunakan satu arah dari yang lain?

Subhaa Chandar
sumber
3
Mereka tidak setara. Lihat jawaban yang bagus dari Icarus. Sebagai contoh, perhatikan kasus yang ada ./file ada tetapi tidak dapat dibaca.
AlexP
Terkait: Kapan 'jika' tidak perlu? dan mengapa “jika” ini tidak cukup berhasil?
G-Man Mengatakan 'Reinstate Monica'

Jawaban:

26

Tidak, konstruksi if A; then B; else C; fidan A && B || Cyang tidak setara .

Dengan if A; then B; else C; fi, perintah Aselalu dievaluasi dan dieksekusi (setidaknya upaya untuk mengeksekusi dibuat) dan kemudian perintah Batau perintah Cdievaluasi dan dieksekusi.

Dengan A && B || C, itu sama untuk perintah Adan Btetapi berbeda untuk C: perintah Cdievaluasi dan dijalankan jika baik A gagal atau B gagal.

Dalam contoh Anda, anggaplah Anda chmod u-r ./myfile, maka, meskipun [ -f ./myfile ]berhasil, Anda akan melakukannyacat /home/user/myfile

Saran saya: gunakan A && Batau A || Bsemua yang Anda inginkan, ini tetap mudah dibaca dan dipahami dan tidak ada jebakan. Tetapi jika Anda maksud jika ... maka ... lain ... kemudian gunakan if A; then B; else C; fi.

Xhienne
sumber
29

Kebanyakan orang merasa lebih mudah untuk memahami if... then... else... fibentuk.

Untuk itu a && b || c, Anda harus memastikan bahwa bmengembalikan true. Ini adalah penyebab bug halus dan merupakan alasan bagus untuk menghindari gaya ini. Jika b tidak mengembalikan true, ini tidak sama.

 $ if true; then false ; else echo boom ; fi
 $ true && false || echo boom
 boom

Untuk pengujian dan tindakan yang sangat singkat yang tidak memiliki klausa lain, panjang yang pendek itu menarik, misalnya

 die(){ printf "%s: %s\n" "$0" "$*" >&2 ; exit 1; }

 [ "$#" -eq 2] || die "Needs 2 arguments, input and output"

 if [ "$#" -ne 2 ] ; then
     die "Needs 2 arguments, input and output"
 fi

&&dan ||yang short circuiting operators, segera setelah hasilnya diketahui tes yang tidak dibutuhkan lanjut yang dilewati. a && b || cdikelompokkan sebagai (a && b) || c. Pertama adijalankan. Jika itu failsyang didefinisikan sebagai tidak mengembalikan status keluar dari 0, maka grup (a && b)diketahui faildan btidak perlu dijalankan. Tidak ||tahu hasil dari ekspresi sehingga perlu dieksekusi c. Jika aberhasil (mengembalikan nol) maka &&operator belum mengetahui hasil a && bsehingga harus lari buntuk mencari tahu. Jika bberhasil maka a && bberhasil dan yang ||tahu hasil keseluruhan adalah sukses, jadi tidak perlu dijalankan c. Jika bgagal maka||masih belum tahu nilai ekspresi, jadi perlu dijalankan c.

icarus
sumber
7

Operator && menjalankan perintah berikutnya jika perintah sebelumnya berhasil, (kode keluar yang dikembalikan ($?) 0 = logis benar).

Dalam bentuk A && B || C, perintah (atau kondisi) A dievaluasi dan jika A mengembalikan true (sukses, keluar kode 0) maka perintah B dijalankan. Jika A gagal (dengan demikian akan mengembalikan kode keluar - palsu selain 0) dan / atau B gagal (kembali salah ) maka perintah C akan dieksekusi.

Juga &&operator digunakan sebagai DAN dalam kondisi cek dan operator ||bekerja seperti ATAU dalam kondisi cek.

Bergantung pada apa yang ingin Anda lakukan dengan skrip Anda, formulir A && B || Cdapat digunakan untuk pemeriksaan kondisi seperti contoh Anda atau dapat digunakan untuk rantai perintah dan memastikan serangkaian perintah yang akan dieksekusi jika perintah sebelumnya memiliki kode keluar 0 yang berhasil .
Inilah sebabnya mengapa itu adalah umum untuk melihat perintah seperti:
do_something && do_something_else_that_depended_on_something.

Contoh:
apt-get update && apt-get upgrade Jika pembaruan gagal maka pemutakhiran tidak dilakukan, (masuk akal di dunia nyata ...).

mkdir test && echo "Something" > test/file
Bagian echo "Something"akan dieksekusi hanya jika mkdir testberhasil dan operasi mengembalikan kode keluar 0 .

./configure --prefix=/usr && make && sudo make install
Biasanya ditemukan pada kompilasi pekerjaan untuk menghubungkan perintah yang diperlukan secara bersamaan.

Jika Anda mencoba menerapkan "rantai" di atas dengan if - then - else Anda akan membutuhkan lebih banyak perintah dan pemeriksaan (dan dengan demikian lebih banyak kode untuk ditulis - lebih banyak hal yang salah) untuk tugas yang sederhana.

Juga, ingatlah bahwa perintah berantai dengan && dan || dibaca oleh shell dari kiri ke kanan. Anda mungkin perlu mengelompokkan perintah dan memeriksa kondisi dengan tanda kurung untuk bergantung pada langkah berikutnya pada keluaran yang berhasil dari beberapa perintah sebelumnya. Sebagai contoh lihat ini:

root@debian:$ true || true && false;echo $?
1 
#read from left to right
#true OR true=true AND false = false = exit code 1=not success

root@debian:$ true || (true && false);echo $?
0 
# true OR (true AND false)=true OR false = true = exit code 0 = success

Atau contoh kehidupan nyata:

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 ]] && [[ $c -eq 2 ]];echo $?
1 
#condition $a = true OR condition b = true AND condition $c = false
#=> yields false as read from left to right, thus exit code=1 = not ok

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 && $c -eq 2 ]];echo $?
0 
#vars b and c are checked in a group which returns false, 
#condition check of var a returns true, thus true OR false yields true = exit code 0

Perlu diingat bahwa beberapa perintah mengembalikan kode keluar yang berbeda tergantung pada proses yang dijalankan, atau mengembalikan kode yang berbeda tergantung pada tindakan mereka, (misalnya perintah GNU diff, mengembalikan 1 jika dua file berbeda, dan 0 jika tidak). Perintah semacam itu perlu diperlakukan dengan hati-hati di && dan || .

Juga hanya untuk memiliki semua teka-teki bersama, pikirkan gabungan perintah menggunakan ;operator. Dengan format A;B;Csemua perintah akan dieksekusi secara seri tidak peduli apa kode keluar dari perintah Adan B.

George Vasiliou
sumber
1

Banyak kebingungan tentang hal ini mungkin disebabkan oleh dokumentasi bash yang memanggil daftar AND dan OR ini . Meskipun secara logika mirip dengan &&dan ||ditemukan di dalam kurung siku, fungsinya berbeda.

Beberapa contoh dapat menggambarkan hal terbaik ini ...

CATATAN: Kurung kotak tunggal dan ganda ( [ ... ]dan [[ ... ]]) adalah perintah dalam dirinya sendiri yang melakukan perbandingan dan mengembalikan kode keluar. Mereka sebenarnya tidak membutuhkan if.

cmda  && cmdb  || cmdc

Jika cmdakeluar benar, cmdbdieksekusi.
Jika cmdakeluar salah, cmdbTIDAK dijalankan, tetapi cmdc.

cmda; cmdb  && cmdc  || cmdd

Bagaimana cmdajalan keluar diabaikan.
Jika cmdbkeluar benar, cmdcdieksekusi.
Jika cmdbkeluar salah, cmdcTIDAK dijalankan dan cmddtidak.

cmda  && cmdb; cmdc

Jika cmdakeluar benar, cmdbdijalankan, diikuti oleh cmdc.
Jika cmdakeluar salah, cmdbTIDAK dijalankan tetapi cmdcsedang.

Hah? Kenapa cmdcdieksekusi?
Karena bagi penerjemah, tanda titik koma ( ;) dan baris baru memiliki arti yang persis sama. Bash melihat baris kode itu sebagai ...

cmda  && cmdb
cmdc  

Untuk mencapai apa yang diharapkan, kita harus menyertakan cmdb; cmdckurung kurawal di dalam untuk menjadikannya Perintah Kompon (perintah grup) . Titik koma penghentian tambahan hanyalah persyaratan { ...; }sintaksis. Jadi kita dapatkan ...

cmda && { cmdb; cmdc; }
Jika cmdakeluar benar, cmdbdijalankan, diikuti oleh cmdc.
Jika cmdakeluar salah, tidak ada cmdbatau cmdcdieksekusi.
Eksekusi berlanjut dengan baris berikutnya.

Pemakaian

Daftar perintah bersyarat paling berguna untuk mengembalikan sesegera mungkin dari fungsi dan dengan demikian menghindari menafsirkan dan mengeksekusi banyak kode yang tidak perlu. Meskipun demikian, beberapa fungsi kembali, bahwa seseorang harus menjadi obsesif tentang menjaga fungsi tetap pendek sehingga lebih mudah untuk memastikan bahwa semua kondisi yang mungkin tercakup.

Ini adalah contoh dari beberapa kode yang sedang berjalan ...

fnInit () {
  :
  _fn="$1"
  ### fnInit "${FUNCNAME}" ...
  ### first argument MUST be name of the calling function
  #
  [[ "$2" == "--help-all" ]]  && { helpAll                      ; return 0; }
  ### pick from list of functions
  #
  [[ "$2" == "--note-all" ]]  && { noteAll                      ; return 0; }
  ### pick from notes in METAFILE
  #
  [[ "$2" == "--version"  ]]  && { versionShow "${_fn}" "${@:3}"; return 0; }
  #
  [[ "$2" == "--function" ]]  && {
    isFnLoaded "$3"           && { "${@:3}"                     ; return 0; }
    #
    errorShow functionnotfound "Unknown function:  $3"
    return 0
  }
  ### call any loaded function
  #
  [[ "$2" == "--help" || "$2" == "-h" ]]  && { noteShow "$_fn" "${@:3}"; return 0; }
  ### fnInit "${FUNCNAME}" --help or -h
  #
  return 1
}
DocSalvager
sumber