Perbedaan antara [[expr1 || expr2]] dan [[expr1]] || [[expr2]]

6

Pertimbangkan dua ekspresi kondisional expr1dan expr2, misalnya $i -eq $jdan $k -eq $l. Kita dapat menulis ini dalam bashbeberapa cara. Berikut ini dua kemungkinan

[[ expr1 || expr2 ]]

[[ expr1 ]] || [[ expr2 ]]

Saya cukup yakin saya telah melihat rekomendasi di sini bahwa yang kedua harus lebih disukai, tetapi saya tidak dapat menemukan bukti untuk mendukung ini.

Berikut ini contoh skrip yang tampaknya menunjukkan tidak ada perbedaan:

for i in 0 1
do
  for j in 0 1
  do
    for k in 0 1
    do
      for l in 0 1
      do
        if [[ $i -eq $j || $k -eq $l ]]; then printf "1-yes\t"; else printf "1-no\t"; fi
        if [[ $i -eq $j ]] || [[ $k -eq $l ]]; then printf "2-yes\n"; else printf "2-no\n"; fi
      done
    done
  done
done

dan output menunjukkan bahwa kedua konstruk kondisi menghasilkan hasil yang sama:

1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes

Apakah ada manfaat menggunakan satu konstruksi di atas yang lain?

Untuk poin bonus, pertanyaan yang sama tetapi digeneralisasikan ke berbagai kondisi menggunakan ||dan &&. Sebagai contoh [[ expr1 && expr2 || expr3 ]],.

roaima
sumber
Sehubungan dengan mengapa rekomendasi jenis ini sering diberikan wrt. [atau test( tidak [[ ), cari OBtag (tertaut ke definisi "usang") dalam spesifikasi POSIX untuktest . Dalam test, ada beberapa kasus patologis di mana tidak mungkin untuk mengetahui apakah (atau )dimaksudkan untuk menjadi sintaks yang berarti untuk perintah pengujian atau string yang akan diuji, sehingga orang yang mengabaikan penanda keusangan dan menggunakan sintaksis itu sebenarnya dapat membutuhkan sebaliknya. "x$foo"praktik usang ; dengan [[, itu bukan masalah.
Charles Duffy

Jawaban:

7

Saya pikir rekomendasi yang Anda lihat adalah untuk POSIX sh dan / atau testperintah yang berfungsi sebagai [perintah, daripada [[konstruk yang muncul di ksh (terima kasih Stéphane Chazelas untuk tipnya) dan juga digunakan misalnya dalam bash, zsh dan beberapa lainnya kerang.

Dalam sebagian besar bahasa, seperti C, ketika klausa sudah diketahui benar atau salah, tidak perlu mengevaluasi bagian-bagian sisanya tergantung pada operasi: jika benar bukan setelah logis atau mengikutinya, jika salah bukan setelah logis dan , dll. Ini tentu saja memungkinkan misalnya untuk berhenti ketika pointer NULL dan tidak mencoba untuk men-dereference-kannya pada klausa berikutnya.

Tapi sh 's [ expr1 -o expr2 ]konstruk (termasuk implementasi bash) tidak melakukan hal ini: selalu mengevaluasi kedua belah pihak, ketika salah satu ingin hanya expr1 untuk dievaluasi. Ini mungkin dilakukan untuk kompatibilitas dengan testimplementasi perintah. Di sisi lain, sh ||dan &&ikuti prinsip yang biasa: tidak dievaluasi jika tidak akan mengubah hasilnya.

Jadi perbedaan yang perlu diperhatikan adalah:

: > /tmp/effect #clear effect
if [ 1 -eq 1 -o $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

yang menghasilkan:

true
or-was-evaluated

Di atas, masing [- masing bisa diganti dengan /usr/bin/[yang merupakan alias untuk testperintah, digunakan sebelum [dibuat built-in ke shell.

Sementara dua konstruksi berikutnya:

: > /tmp/effect #clear effect
if [ 1 -eq 1 ] || [ $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

atau

: > /tmp/effect #clear effect
if [[ 1 -eq 1 || $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]]; then
    echo true;
fi
cat /tmp/effect

Hanya akan menghasilkan truedan membiarkan effectkosong: ||berperilaku dengan benar, dan [[memperbaiki masalah ini juga.


MEMPERBARUI:

Ketika @ StéphaneChazelas berkomentar, saya melewatkan beberapa perbedaan terkait dengan pertanyaan awal. Saya hanya akan menempatkan di sini yang paling penting (setidaknya bagi saya): prioritas operator.

Sementara shell tidak akan mempertimbangkan prioritas:

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

hasil (karena tidak ada prioritas dan dengan demikian pertama true || truedievaluasi, dan kemudian && false):

false

di [[ ]]dalam &&operator lebih diutamakan daripada ||:

if [[ 1 -eq 1 || 1 -eq 1 && 1 -eq 0 ]]; then
    echo true
else
    echo false
fi

hasil (karena 1 -eq 1 && 1 -eq 0dikelompokkan dan dengan demikian anggota ke-2 ||):

true

setidaknya untuk ksh , bash , zsh .

Jadi [[ ]]telah meningkatkan perilaku baik dari [ ]operator shell langsung dan logis.

AB
sumber
1
[ x -o y ]tidak harus mengevaluasi kedua belah pihak. Sementara GNU testatau [builtin of bashdo, Anda akan menemukannya dengan strace zsh -c '[ x -o -f /x ]'itu [tidak mencoba untuk stat () /x. Sama dengan mksh. Tetapi memang benar bahwa -odan -asangat rusak, tidak dapat digunakan dengan andal dan ditinggalkan oleh POSIX.
Stéphane Chazelas
1
Satu perbedaan adalah bahwa di dalam [[...]], &&memiliki prioritas lebih tinggi daripada ||, sementara di luar mereka memiliki prioritas yang sama. Bandingkan ksh -c '[[ x || x && "" ]]'vssh -c 'true || true && false'
Stéphane Chazelas
1
Perhatikan bahwa standar [/ testutilitas tidak memiliki ==operator. Operator kesetaraan adalah =(perhatikan bahwa di dalam ksh [[...]], =/ ==bukan operator kesetaraan, tetapi yang cocok dengan pola).
Stéphane Chazelas
1
Itu sebaliknya. &&lebih diutamakan di ||dalam [[...]](atau ((...))) tetapi tidak &&dan ||operator shell. [[ x || y && z ]]adalah x || (y && z)sementara x || y && zadalah (x || y) && z(operator dievaluasi kiri ke kanan tanpa didahulukan). Mirip dengan bagaimana *mendahului +( 1 + 2 * 3adalah 1 + (2 * 3)) tapi +dan -memiliki prioritas yang sama.
Stéphane Chazelas
1
Jika &&telah didahulukan ||, maka itu seharusnya true || (true && false), bukannya(true || true) && false
Prvt_Yadav