Saya mencoba untuk memahami bagaimana presedensi operator logis bekerja di bash. Sebagai contoh, saya akan berharap, bahwa perintah berikut tidak menggemakan apa pun.
true || echo aaa && echo bbb
Namun, bertentangan dengan harapan saya, bbbdicetak.
Bisakah seseorang tolong jelaskan, bagaimana saya bisa memahami senyawa &&dan ||operator di bash?
Dalam banyak bahasa komputer, operator dengan prioritas yang sama bersifat asosiatif kiri . Artinya, dengan tidak adanya struktur pengelompokan, operasi paling kiri dijalankan terlebih dahulu. Bash tidak terkecuali pada aturan ini.
Ini penting karena, dalam Bash, &&dan ||memiliki prioritas yang sama.
Jadi yang terjadi dalam contoh Anda adalah bahwa operasi paling kiri ( ||) dilakukan terlebih dahulu:
true || echo aaa
Karena truejelas benar, ||hubung singkat operator dan seluruh pernyataan dianggap benar tanpa perlu mengevaluasi echo aaaseperti yang Anda harapkan. Sekarang tinggal melakukan operasi paling kanan:
(...)&& echo bbb
Karena operasi pertama dievaluasi menjadi true (yaitu memiliki status keluar 0), seolah-olah Anda sedang mengeksekusi
true && echo bbb
sehingga &&tidak akan mengalami hubungan pendek, itulah sebabnya Anda lihat bbbbergema.
Anda akan mendapatkan perilaku yang sama dengannya
false && echo aaa || echo bbb
Catatan berdasarkan komentar
Anda harus mencatat bahwa aturan associativity kiri hanya diikuti ketika kedua operator memiliki prioritas yang sama . Hal ini tidak terjadi ketika Anda menggunakan operator ini dalam hubungannya dengan kata kunci seperti [[...]]atau ((...))atau gunakan -odan -aoperator sebagai argumen untuk testatau [perintah. Dalam kasus seperti itu, AND ( &&atau -a) diutamakan daripada OR ( ||atau -o). Terima kasih atas komentar Stephane Chazelas karena telah menjelaskan poin ini.
Tampaknya dalam bahasa C dan C-like &&memiliki prioritas lebih tinggi daripada ||yang mungkin mengapa Anda mengharapkan konstruksi asli Anda berperilaku seperti
true ||(echo aaa && echo bbb).
Namun, ini tidak terjadi pada Bash, di mana kedua operator memiliki prioritas yang sama, itulah sebabnya Bash mem-parsing ekspresi Anda menggunakan aturan associativity kiri. Terima kasih atas komentar Kevin karena mengemukakan ini.
Mungkin juga ada kasus di mana ketiga ekspresi dievaluasi. Jika perintah pertama mengembalikan status keluar bukan nol, ||tidak akan terjadi hubungan pendek dan melanjutkan untuk mengeksekusi perintah kedua. Jika perintah kedua kembali dengan status keluar nol, maka &&tidak akan terjadi hubungan pendek juga dan perintah ketiga akan dieksekusi. Terima kasih atas komentar Ignacio Vazquez-Abrams karena mengemukakan ini.
Sebuah catatan untuk menambah sedikit kebingungan: sementara operator &&dan ||shell seperti cmd1 && cmd2 || cmd3memiliki prioritas yang sama, &&in ((...))dan [[...]]memiliki prioritas lebih dari ||( ((a || b && c))is ((a || (b && c)))). Sama berlaku untuk -a/ -odalam test/ [dan finddan &/ |dalam expr.
Stéphane Chazelas
16
Dalam bahasa c-like, &&lebih diutamakan daripada ||, sehingga perilaku yang diharapkan OP akan terjadi. Pengguna yang tidak terbiasa dengan bahasa tersebut mungkin tidak menyadari bahwa dalam bash mereka memiliki prioritas yang sama, jadi mungkin lebih baik menunjukkan secara eksplisit bahwa mereka memiliki prioritas yang sama dalam bash.
Kevin
Perhatikan juga bahwa ada situasi di mana ketiga perintah dapat dijalankan, yaitu jika perintah tengah dapat mengembalikan benar atau salah.
Ignacio Vazquez-Abrams
@Kevin Silakan periksa jawaban yang diedit.
Joseph R.
1
Hari ini, bagi saya, hit Google teratas untuk "bash operator precedence" adalah tldp.org/LDP/abs/html/opprecedence.html ... yang mengklaim bahwa && memiliki prioritas lebih tinggi daripada ||. Namun @ Josephos. jelas benar de facto dan de jure juga. Dash berperilaku sama dan mencari "diutamakan" di pubs.opengroup.org/onlinepubs/009695399/utilities/… , saya melihat ini persyaratan POSIX, jadi kita bisa bergantung padanya. Semua yang saya temukan untuk pelaporan bug adalah alamat email penulis, yang pasti telah dipecat hingga mati dalam enam tahun terakhir. Saya akan tetap mencoba ...
Martin Dorey
62
Jika Anda ingin banyak hal bergantung pada kondisi Anda, kelompokkan:
true ||{ echo aaa && echo bbb;}
Itu tidak mencetak apa-apa, sementara
true &&{ echo aaa && echo bbb;}
mencetak kedua string.
Alasan mengapa hal ini terjadi jauh lebih sederhana daripada yang dilakukan Joseph. Ingat apa yang dilakukan Bash dengan ||dan &&. Ini semua tentang status pengembalian perintah sebelumnya. Cara literal untuk melihat perintah mentah Anda adalah:
( true || echo aaa )&& echo bbb
Perintah pertama ( true || echo aaa) keluar dengan 0.
+1 Untuk mencapai hasil yang diinginkan OP. Perhatikan bahwa tanda kurung yang Anda tempatkan (true || echo aaa) && echo bbbpersis seperti yang saya hasilkan.
Joseph R.
1
Senang melihat @Oli di sisi dunia ini.
Braiam
7
Perhatikan semi-kolom ;di bagian paling akhir. Tanpa ini, itu tidak akan berhasil!
Serge Stroobandt
2
Saya mengalami masalah dengan ini karena saya kehilangan semi-kolon setelah perintah terakhir (misalnya echo bbb;dalam contoh pertama). Setelah saya tahu bahwa saya kehilangan itu, jawaban ini persis apa yang saya cari. +1 untuk membantu saya mencari tahu bagaimana mencapai apa yang saya inginkan!
The &&dan ||operator tidak membalas penggantian inline untuk if-then-else. Meskipun jika digunakan dengan hati-hati, mereka dapat melakukan banyak hal yang sama.
Sebuah tes tunggal mudah dan jelas ...
[[ A == A ]]&& echo TRUE # TRUE[[ A == B ]]&& echo TRUE # [[ A == A ]]|| echo FALSE # [[ A == B ]]|| echo FALSE # FALSE
Namun, mencoba menambahkan beberapa tes dapat menghasilkan hasil yang tidak terduga ...
[[ A == A ]]&& echo TRUE || echo FALSE # TRUE (as expected)[[ A == B ]]&& echo TRUE || echo FALSE # FALSE (as expected)[[ A == A ]]|| echo FALSE && echo TRUE # TRUE (as expected)[[ A == B ]]|| echo FALSE && echo TRUE # FALSE TRUE (huh?)
Mengapa FALSE dan TRUE digemakan?
Apa yang terjadi di sini adalah bahwa kita tidak menyadari itu &&dan ||adalah operator kelebihan beban yang bertindak berbeda di dalam kurung uji bersyarat [[ ]]daripada yang mereka lakukan dalam daftar DAN dan ATAU (eksekusi bersyarat) yang kita miliki di sini.
Dari halaman bash (diedit) ...
Daftar
Daftar adalah urutan satu atau beberapa jaringan pipa yang dipisahkan oleh salah satu operator;, &, &&, atau ││, dan secara opsional diakhiri oleh salah satu dari,, &, atau. Dari daftar operator ini, && dan ││ memiliki prioritas yang sama, diikuti oleh; dan &, yang memiliki prioritas sama.
Urutan satu atau lebih baris baru mungkin muncul dalam daftar alih-alih tanda titik koma untuk membatasi perintah.
Jika perintah diakhiri oleh operator kontrol &, shell mengeksekusi perintah di latar belakang dalam sebuah subkulit. Shell tidak menunggu perintah selesai, dan status kembali adalah 0. Perintah dipisahkan oleh a; dieksekusi secara berurutan; shell menunggu setiap perintah berakhir pada gilirannya. Status kembali adalah status keluar dari perintah terakhir yang dijalankan.
Daftar AND dan OR adalah urutan dari salah satu dari lebih banyak pipa yang dipisahkan oleh operator kontrol&& dan ││. Daftar AND dan OR dijalankan dengan asosiasi kiri.
Daftar AND memiliki formulir ... command1 && command2
Command2 dijalankan jika, dan hanya jika, command1 mengembalikan status keluar nol.
Daftar ATAU memiliki bentuk ... command1 ││ command2
Command2 dijalankan jika dan hanya jika command1 mengembalikan status keluar yang tidak nol.
Status pengembalian daftar AND dan OR adalah status keluar dari perintah terakhir yang dijalankan dalam daftar.
Kembali ke contoh terakhir kami ...
[[ A == B ]]|| echo FALSE && echo TRUE
[[ A == B ]] is false
||Does NOT mean OR!It means...'execute next command if last command return code(rc) was false'
echo FALSE The'echo' command rc is always true
(i.e. it successfully echoed the word "FALSE")&&Execute next command if last command rc was true
echo TRUE Since the 'echo FALSE' rc was true,then echo "TRUE"
Baik. Jika itu benar, lalu mengapa contoh berikutnya hingga terakhir menggemakan sesuatu?
[[ A == A ]]|| echo FALSE && echo TRUE
[[ A == A ]] is true
|| execute next command if last command rc was false.
echo FALSE Since last rc was true, shouldn't it have stopped before this?
Nope. Instead, it skips the 'echo FALSE', does not even try to
execute it, and continues looking for a `&&` clause.
&& ... which it finds here
echo TRUE ... so, since `[[ A == A ]]` is true, then it echos "TRUE"
Risiko kesalahan logika saat menggunakan lebih dari satu &&atau ||dalam daftar perintah cukup tinggi.
Rekomendasi
Satu &&atau ||dalam daftar perintah berfungsi seperti yang diharapkan sehingga cukup aman. Jika ini adalah situasi di mana Anda tidak memerlukan klausa lain, sesuatu seperti yang berikut ini bisa lebih jelas untuk diikuti (kurung kurawal diperlukan untuk mengelompokkan 2 perintah terakhir) ...
[[ $1 ==--help ]]&&{ echo "$HELP"; exit;}
Banyak &&dan ||operator, di mana setiap perintah kecuali yang terakhir adalah tes (yaitu di dalam tanda kurung [[ ]]), biasanya juga aman karena semua tetapi operator terakhir berperilaku seperti yang diharapkan. Operator terakhir bertindak lebih seperti a thenatau elseklausa.
Saya juga bingung dengan ini, tetapi beginilah cara saya berpikir tentang cara Bash membaca pernyataan Anda (saat membaca simbol dari kiri ke kanan):
Simbol yang ditemukan true. Ini perlu dievaluasi setelah akhir perintah tercapai. Pada titik ini, tidak tahu apakah ada argumen. Simpan perintah dalam buffer eksekusi.
Simbol yang ditemukan ||. Perintah sebelumnya sekarang selesai, jadi evaluasilah. Command (buffer) dieksekusi: true. Hasil evaluasi: 0 (yaitu kesuksesan). Simpan hasil 0 dalam register "evaluasi terakhir". Sekarang perhatikan simbol ||itu sendiri. Ini tergantung pada hasil evaluasi terakhir yang bukan nol. Memeriksa register "evaluasi terakhir" dan menemukan 0. Karena 0 bukan bukan nol, perintah berikut ini tidak perlu dievaluasi.
Simbol yang ditemukan echo. Dapat mengabaikan simbol ini, karena perintah berikut ini tidak perlu dievaluasi.
Simbol yang ditemukan aaa. Ini adalah argumen untuk perintah echo(3), tetapi karena echo(3) tidak perlu dievaluasi, ini dapat diabaikan.
Simbol yang ditemukan &&. Ini tergantung pada hasil evaluasi terakhir menjadi nol. Memeriksa register "evaluasi terakhir" dan menemukan 0. Karena 0 adalah nol, perintah berikut ini perlu dievaluasi.
Simbol yang ditemukan echo. Perintah ini perlu dievaluasi setelah akhir perintah tercapai, karena perintah berikut memang perlu dievaluasi. Simpan perintah dalam buffer eksekusi.
Simbol yang ditemukan bbb. Ini adalah argumen untuk perintah echo(6). Karena echomemang perlu dievaluasi, tambahkan bbbke eksekusi buffer.
Mencapai ujung garis. Perintah sebelumnya sekarang lengkap dan memang perlu dievaluasi. Command (buffer) dieksekusi: echo bbb. Hasil evaluasi: 0 (yaitu kesuksesan). Simpan hasil 0 dalam register "evaluasi terakhir".
Dan tentu saja, langkah terakhir menyebabkan bbbdigemakan ke konsol.
&&
dan||
shell seperticmd1 && cmd2 || cmd3
memiliki prioritas yang sama,&&
in((...))
dan[[...]]
memiliki prioritas lebih dari||
(((a || b && c))
is((a || (b && c)))
). Sama berlaku untuk-a
/-o
dalamtest
/[
danfind
dan&
/|
dalamexpr
.&&
lebih diutamakan daripada||
, sehingga perilaku yang diharapkan OP akan terjadi. Pengguna yang tidak terbiasa dengan bahasa tersebut mungkin tidak menyadari bahwa dalam bash mereka memiliki prioritas yang sama, jadi mungkin lebih baik menunjukkan secara eksplisit bahwa mereka memiliki prioritas yang sama dalam bash.Jika Anda ingin banyak hal bergantung pada kondisi Anda, kelompokkan:
Itu tidak mencetak apa-apa, sementara
mencetak kedua string.
Alasan mengapa hal ini terjadi jauh lebih sederhana daripada yang dilakukan Joseph. Ingat apa yang dilakukan Bash dengan
||
dan&&
. Ini semua tentang status pengembalian perintah sebelumnya. Cara literal untuk melihat perintah mentah Anda adalah:Perintah pertama (
true || echo aaa
) keluar dengan0
.sumber
(true || echo aaa) && echo bbb
persis seperti yang saya hasilkan.;
di bagian paling akhir. Tanpa ini, itu tidak akan berhasil!echo bbb;
dalam contoh pertama). Setelah saya tahu bahwa saya kehilangan itu, jawaban ini persis apa yang saya cari. +1 untuk membantu saya mencari tahu bagaimana mencapai apa yang saya inginkan!(...)
dan{...}
notasi: petunjuk pengelompokan perintahThe
&&
dan||
operator tidak membalas penggantian inline untuk if-then-else. Meskipun jika digunakan dengan hati-hati, mereka dapat melakukan banyak hal yang sama.Sebuah tes tunggal mudah dan jelas ...
Namun, mencoba menambahkan beberapa tes dapat menghasilkan hasil yang tidak terduga ...
Mengapa FALSE dan TRUE digemakan?
Apa yang terjadi di sini adalah bahwa kita tidak menyadari itu
&&
dan||
adalah operator kelebihan beban yang bertindak berbeda di dalam kurung uji bersyarat[[ ]]
daripada yang mereka lakukan dalam daftar DAN dan ATAU (eksekusi bersyarat) yang kita miliki di sini.Dari halaman bash (diedit) ...
Kembali ke contoh terakhir kami ...
Baik. Jika itu benar, lalu mengapa contoh berikutnya hingga terakhir menggemakan sesuatu?
Risiko kesalahan logika saat menggunakan lebih dari satu
&&
atau||
dalam daftar perintah cukup tinggi.Rekomendasi
Satu
&&
atau||
dalam daftar perintah berfungsi seperti yang diharapkan sehingga cukup aman. Jika ini adalah situasi di mana Anda tidak memerlukan klausa lain, sesuatu seperti yang berikut ini bisa lebih jelas untuk diikuti (kurung kurawal diperlukan untuk mengelompokkan 2 perintah terakhir) ...Banyak
&&
dan||
operator, di mana setiap perintah kecuali yang terakhir adalah tes (yaitu di dalam tanda kurung[[ ]]
), biasanya juga aman karena semua tetapi operator terakhir berperilaku seperti yang diharapkan. Operator terakhir bertindak lebih seperti athen
atauelse
klausa.sumber
Saya juga bingung dengan ini, tetapi beginilah cara saya berpikir tentang cara Bash membaca pernyataan Anda (saat membaca simbol dari kiri ke kanan):
true
. Ini perlu dievaluasi setelah akhir perintah tercapai. Pada titik ini, tidak tahu apakah ada argumen. Simpan perintah dalam buffer eksekusi.||
. Perintah sebelumnya sekarang selesai, jadi evaluasilah. Command (buffer) dieksekusi:true
. Hasil evaluasi: 0 (yaitu kesuksesan). Simpan hasil 0 dalam register "evaluasi terakhir". Sekarang perhatikan simbol||
itu sendiri. Ini tergantung pada hasil evaluasi terakhir yang bukan nol. Memeriksa register "evaluasi terakhir" dan menemukan 0. Karena 0 bukan bukan nol, perintah berikut ini tidak perlu dievaluasi.echo
. Dapat mengabaikan simbol ini, karena perintah berikut ini tidak perlu dievaluasi.aaa
. Ini adalah argumen untuk perintahecho
(3), tetapi karenaecho
(3) tidak perlu dievaluasi, ini dapat diabaikan.&&
. Ini tergantung pada hasil evaluasi terakhir menjadi nol. Memeriksa register "evaluasi terakhir" dan menemukan 0. Karena 0 adalah nol, perintah berikut ini perlu dievaluasi.echo
. Perintah ini perlu dievaluasi setelah akhir perintah tercapai, karena perintah berikut memang perlu dievaluasi. Simpan perintah dalam buffer eksekusi.bbb
. Ini adalah argumen untuk perintahecho
(6). Karenaecho
memang perlu dievaluasi, tambahkanbbb
ke eksekusi buffer.echo bbb
. Hasil evaluasi: 0 (yaitu kesuksesan). Simpan hasil 0 dalam register "evaluasi terakhir".Dan tentu saja, langkah terakhir menyebabkan
bbb
digemakan ke konsol.sumber