Beberapa operator logika, ((A || B) && C), dan “kesalahan sintaksis dekat token yang tak terduga”

24

Saya bekerja dengan Bash 3, dan saya mencoba untuk membentuk bersyarat. Dalam C / C ++, sederhana mati nya: ((A || B) && C). Di Bash, ternyata tidak begitu (saya pikir para penulis Git harus menyumbang kode ini sebelum mereka pindah ke upaya lain).

Ini tidak bekerja. Perhatikan bahwa <0 or 1>ini bukan string literal; itu berarti 0 atau 1 (umumnya berasal dari grep -i).

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eqe "0" ]; then ... fi

Ini menghasilkan:

line 322: syntax error near unexpected token `[['

Saya kemudian mencoba:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ ([ "$A" -eq "0" ]) || ([ "$B" -ne "0" ]) ] && [ "$C" -eq "0" ]; then ... fi

itu menghasilkan:

line 322: syntax error near unexpected token `[['

Bagian dari masalah adalah hasil pencarian adalah contoh-contoh sepele, dan bukan contoh yang lebih kompleks dengan kondisional majemuk.

Bagaimana cara melakukan yang sederhana ((A || B) && C)di Bash?


Saya siap untuk membuka gulungannya dan mengulangi perintah yang sama di beberapa blok:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>

if [ "$A" -eq "0" ] && [ "$C" -eq "0" ]; then
    ...
elif [ "$B" -ne "0" ] && [ "$C" -eq "0" ]; then
    ... 
fi

sumber

Jawaban:

42

Sintaks dari bash bukan seperti-C, bahkan jika sebagian kecil diilhami oleh C. Anda tidak bisa hanya mencoba menulis kode C dan mengharapkannya bekerja.

Poin utama dari shell adalah menjalankan perintah. Perintah braket terbuka [adalah perintah, yang melakukan satu pengujian¹. Anda bahkan dapat menuliskannya sebagai test(tanpa braket penutup akhir). The ||dan &&operator operator shell, mereka menggabungkan perintah , tidak tes.

Jadi, ketika Anda menulis

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ]

itu diurai sebagai

[ [ "$A" -eq "0" ] ||
[ "$B" -ne "0" ] ] &&
[ "$C" -eq "0" ]

yang sama dengan

test [ "$A" -eq "0" ||
test "$B" -ne "0" ] &&
test "$C" -eq "0"

Perhatikan tanda kurung yang tidak seimbang? Ya, itu tidak baik. Upaya Anda dengan tanda kurung memiliki masalah yang sama: kurung palsu.

Sintaks untuk mengelompokkan perintah bersama adalah kawat gigi. Cara kurung diurai memerlukan perintah yang lengkap di depannya, jadi Anda harus menghentikan perintah di dalam kurung dengan garis baru atau titik koma.

if { [ "$A" -eq "0" ] || [ "$B" -ne "0" ]; } && [ "$C" -eq "0" ]; then 

Ada cara alternatif yaitu menggunakan kurung ganda. Tidak seperti kurung tunggal, kurung ganda adalah sintaksis shell khusus. Mereka membatasi ekspresi kondisional . Di dalam tanda kurung ganda, Anda dapat menggunakan tanda kurung dan operator seperti &&dan ||. Karena kurung ganda adalah sintaksis shell, shell mengetahui bahwa ketika operator ini berada di dalam kurung, mereka adalah bagian dari sintaksis ekspresi kondisional, bukan bagian dari sintaksis perintah shell biasa.

if [[ ($A -eq 0 || $B -ne 0) && $C -eq 0 ]]; then 

Jika semua tes Anda numerik, masih ada cara lain, yang membatasi ekspresi artihmatika . Ekspresi aritmatika melakukan perhitungan integer dengan sintaks yang sangat mirip C.

if (((A == 0 || B != 0) && C == 0)); then 

Anda mungkin menganggap primer bash braket saya bermanfaat.

[dapat digunakan dalam sh biasa. [[dan ((khusus untuk bash (dan ksh dan zsh).

¹ Ini juga dapat menggabungkan beberapa tes dengan operator boolean, tetapi ini rumit untuk digunakan dan memiliki jebakan halus sehingga saya tidak akan menjelaskannya.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Terima kasih Giles. Apakah ini berlaku untuk Bash 2 hingga Bash 4? Saya butuh sesuatu yang mudah dibawa-bawa, tetapi Kusalananda menyebutkan beberapa hal yang tidak saya lakukan bersifat portabel (apakah itu berarti apa yang saya lakukan bukan portabel?). Di sini, agak berarti Bash 2 hingga Bash 4.
@jww [[ … ]]ditambahkan dalam bash 2.02. ((…))telah ditambahkan di bash 2.0.
Gilles 'SANGAT berhenti menjadi jahat'
@ Giles - terima kasih. Bash 2 mungkin menggelikan bagi sebagian besar. Ini karena tata kelola kami, dan keengganan untuk meninggalkan platform lama. Bash 2 dan GCC 3 muncul pada pengujian Fedora 1 kami. Kami akan membiarkan platform yang lebih tua pergi pada kesempatan, tetapi harus ada alasan untuk itu. Kami tidak berlangganan kebijakan ditinggalkan, Apple, Microsoft, Mozilla, dll.
@jww Harap mengerti bahwa prioritas ||dan &&berubah dari [ke [[dan ((. Diutamakan sama dalam [(gunakan tanda kurung untuk mengontrol urutan evaluasi), tetapi && memiliki prioritas lebih tinggi di dalam [[dan ((daripada ||(seperti dalam C).
6

Gunakan [[:

if [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]; then ...

Jika Anda suka [, yang berikut ini berfungsi:

if [ \( "$A" -eq "0" -o "$B" -ne "0" \) -a "$C" -eq "0" ]; then ...
Stephen Kitt
sumber
3

Gunakan -ooperator alih-alih bersarang | |. Anda juga dapat menggunakan -auntuk mengganti && jika diperlukan dalam laporan Anda yang lain.

   EXPRESSION1 -a EXPRESSION2
          both EXPRESSION1 and EXPRESSION2 are true

   EXPRESSION1 -o EXPRESSION2
          either EXPRESSION1 or EXPRESSION2 is true


if [  "$A" -eq "0" -o "$B" -ne "0"  ] && [ "$C" -eq "0" ]; then echo success;fi
Zachary Brady
sumber
Terima kasih. Saya menemukan -adan -o, tetapi saya tidak bereksperimen dengan mereka. Seseorang di Stack Overflow mengatakan untuk menghindarinya. Saya sebagian besar setuju dengan mereka karena skrip kurang dapat dibaca menggunakannya.
... tetapi lebih portabel.
Kusalananda
@ Kusalananda - Terima kasih atas bantuan portabilitas. Script harus dijalankan pada sistem dengan Bash 2 hingga Bash 4. Akankah [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]ada masalah pada mereka?
Tidak akan ada masalah selama Anda menyimpan bashatau shell lain yang mengerti [[ ... ]].
Kusalananda