Kabuk mantıksal işleçlerinin önceliği &&, ||


126

Mantıksal işleç önceliğinin bash olarak nasıl çalıştığını anlamaya çalışıyorum. Örneğin, aşağıdaki komutu hiçbir şey yankılanmadığını umuyordum.

true || echo aaa && echo bbb

Ancak beklentilerimin aksine bbbbasılıyor.

Biri lütfen açıklayabilir mi, bash içindeki bileşik &&ve ||operatörleri nasıl anlayabilirim ?

Yanıtlar:


127

Birçok bilgisayar dilinde, aynı önceliğe sahip operatörler sola dayalıdır . Yani, gruplama yapılarının yokluğunda, önce en soldaki işlemler gerçekleştirilir. Bash bu kuralın istisnası değildir.

Bash, çünkü bu önemlidir &&ve ||aynı önceliğe sahiptir.

Öyleyse, örneğinizde olan şey, en soldaki işlemin ( ||) ilk önce gerçekleştirildiği:

true || echo aaa

Yana truekuşkusuz doğrudur, ||operatör kısa devreler ve bütün açıklamada değerlendirmek gerek kalmadan gerçek olarak kabul edilir echo aaabeklediğiniz gibi. Şimdi en doğru işlemi yapmak için kalır:

(...) && echo bbb

İlk işlem doğru olarak değerlendirildiğinden (yani 0 çıkış statüsüne sahip olduğundan), işlem yapıyormuşsunuz gibi

true && echo bbb

bu yüzden &&kısa devre olmayacak, bu yüzden bbbyankılanıyorsunuz.

Aynı davranışı elde edersiniz

false && echo aaa || echo bbb

Yorumlara dayalı notlar

  • Sol birliktelik kuralının yalnızca her iki operatör de aynı önceliğe sahip olduğunda uygulandığını unutmayın . Eğer gibi anahtar kelimeler ile birlikte bu operatörler kullandığınızda bu durum böyle değil [[...]]ya ((...))veya kullanım -ove -aargüman olarak operatörler testveya [komutlar. Bu gibi durumlarda, VE ( &&veya -a) VEYA ( ||veya -o) 'dan önceliklidir . Stephane Chazelas'ın bu konuyu açıklığa kavuşturduğu için yaptığı yorum için teşekkürler.
  • Görünüşe göre C ve C benzeri dillerin &&önceliği daha yüksek, ||bu nedenle orijinal yapınızın böyle davranmasını beklediğinizden muhtemelen

    true || (echo aaa && echo bbb). 

    Ancak bu, Bash'in her iki operatörün de aynı önceliğe sahip olduğu durum değildir; bu yüzden Bash, sol birliktelik kuralını kullanarak ifadenizi ayrıştırır. Kevin'in bunu gündeme getirdiği yorumu için teşekkürler.

  • 3 ifadenin de değerlendirildiği durumlar olabilir . İlk komut sıfır olmayan bir çıkış durumu döndürürse, ||kısa devre olmaz ve ikinci komutu yerine getirmeye devam eder. İkinci komut sıfır çıkış durumuyla geri dönerse, o zaman &&kısa devre olmayacak ve üçüncü komut çalıştırılacaktır. Ignacio Vazquez-Abrams'ın bunu gündeme getirdiği için teşekkürler.


26
Biraz daha karışıklık eklemek için bir not:: &&ve ||kabuk operatörleri cmd1 && cmd2 || cmd3aynı önceliğe sahipken, &&giriş ((...))ve çıkış [[...]]önceliği ||( ((a || b && c))is ((a || (b && c)))). Aynı şey -a/ -oin test/ [and findve &/ |in için de geçerlidir expr.
Stéphane Chazelas

16
C benzeri dillerde, &&öncekinden daha fazla öncelik vardır ||, bu nedenle OP'nin beklenen davranışı gerçekleşir. Bu tür dillere alışkın olmayan kullanıcılar bash'te aynı önceliğe sahip olduklarını fark etmeyebilirler, bu yüzden bash'te aynı önceliğe sahip olduklarını açıkça belirtmeye değer olabilir.
Kevin,

Ayrıca, her üç komutun da çalıştırılabileceği durumlar olduğunu unutmayın; örneğin, orta komut doğru ya da yanlış olarak dönebilirse .
Ignacio Vazquez-Abrams,

@Kevin Lütfen düzenlenmiş cevabı kontrol edin.
Joseph R.

1
Bugün, benim için, "bash operatörü önceliği" için en çok aranan Google , &&'nin || den daha yüksek önceliğe sahip olduğunu iddia eden tldp.org/LDP/abs/html/opprecedence.html ... dir. @JosephR. açıkça fiili ve de jüri de doğru. Dash aynı davranır ve pubs.opengroup.org/onlinepubs/009695399/utilities/… adresinde "öncelik" için arama yapar , bunun bir POSIX gereksinimi olduğunu görüyorum, buna bağlı olabiliriz. Hata bildirimi için bulduğum tek şey, yazarın son altı yılda kesinlikle ölümüne yol açan e-posta adresiydi. Yine de deneyeceğim ...
Martin Dorey

62

Durumunuza bağlı olarak birden fazla şey yapmak istiyorsanız, bunları gruplayın:

true || { echo aaa && echo bbb; }

Bu hiçbir şeyi basmazken

true && { echo aaa && echo bbb; }

her iki dizeyi de yazdırır.


Bunun olmasının nedeni, Joseph'in çıkardığından çok daha basit. Bash'in ||ve ile ne yaptığını unutma &&. Hepsi önceki komutun dönüş durumu ile ilgili. Ham komutunuza bakmanın gerçek bir yolu:

( true || echo aaa ) && echo bbb

İlk komut ( true || echo aaa) ile çıkılıyor 0.

$ true || echo aaa; echo $?
0
$ true && echo aaa; echo $?
aaa
0

$ false && echo aaa; echo $?
1
$ false || echo aaa; echo $?
aaa
0

7
+1 OP'nin amaçlanan sonucuna ulaşmak için. Yerleştirdiğiniz parantezlerin (true || echo aaa) && echo bbbtam olarak ne yaptığımı yaptığına dikkat edin.
Joseph R.

1
Dünyanın bu tarafında @Oli'yi görmek güzel.
Braiam,

7
;En sondaki yarı sütuna dikkat edin. Bu olmadan, işe yaramaz!
Serge Stroobandt

2
Bununla başım belaya girdi çünkü son komuttan sonra takip eden yarı kolonu kaçırdım (örneğin echo bbb;ilk örneklerde). Bir zamanlar bunun eksik olduğunu anladım, bu cevap tam olarak aradığım şeydi. İstediğim şeyi nasıl başarabileceğimi bulmama yardım ettiği için +1!
Doktor J

5
Worth tarafından karıştı herkes için okuma (...)ve {...}gösterimde: komut gruplama manuel
Antara

16

&&Ve ||operatörler vardır değil if-then-else için satır içi değiştirmeler kesin. Dikkatli kullanılsalar da aynı şeyi başarabilirler.

Tek bir test basit ve açıktır ...

[[ A == A ]]  && echo TRUE                          # TRUE
[[ A == B ]]  && echo TRUE                          # 
[[ A == A ]]  || echo FALSE                         # 
[[ A == B ]]  || echo FALSE                         # FALSE

Ancak, birden çok test eklemeye çalışmak beklenmedik sonuçlar doğurabilir ...

[[ 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?)

Neden YANLIŞ hem edilir ve DOĞRU yankılandı?

Burada oluyor biz fark ettik ki &&ve ||koşullu sınama ayraç içindeki farklı hareket operatörleri aşırı yüklü [[ ]]onlar AND yapmak ve OR (koşullu yürütme) liste burada var daha.

Bash manpage'den (düzenlenmiş) ...

Listeler

Bir liste, operatörlerden biri tarafından ayrılan bir veya daha fazla boru hattının bir dizisidir;, &, && veya option ve isteğe bağlı olarak;, &, veya. Bu listeden operatörler, && ve ││ eşit önceliğe sahiptir; ve &, eşit önceliğe sahip.

Komutları sınırlandırmak için noktalı virgül yerine bir veya daha fazla yeni satır dizisi bir listede görünebilir.

Bir komut kontrol operatörü & tarafından sonlandırılırsa, kabuk komutu bir alt kabukta arka planda yürütür. Kabuk komutun bitmesini beklemez ve dönüş durumu 0'dır. Komutlar a; sırayla yürütülür; kabuk, her komutun sırayla sonlandırılmasını bekler. Dönüş durumu, yürütülen son komutun çıkış durumudur.

AND ve VEYA listeleri, sırasıyla && ve ││ kontrol operatörleri tarafından ayrılan daha fazla boru hattından birinin sekanslarıdır. AND ve VEYA listeleri sol ilişkilendirmeyle yürütülür.

Bir AND listesi var ...
command1 && command2
Command2, eğer komut1 ise sıfır çıkış durumunu döndürürse çalıştırılır.

Bir OR listesi var ...
command1 ││ command2
Command2, eğer sadece command1 sıfır olmayan bir çıkış durumu verirse çalıştırılır.

AND ve OR listelerinin dönüş durumu, listedeki son komutun çıkış durumudur.

Son örneğimize dönersek ...

[[ 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"

Tamam. Eğer bu doğruysa, son örneğin yanındaki niçin bir şey yansıtıyor?

[[ 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"

Birden fazla &&veya ||bir komut listesinde kullanılırken mantık hata riski oldukça yüksektir.

öneriler

Bir tek &&veya ||bir komut listesinde beklendiği gibi çalışır, bu yüzden oldukça güvenlidir. Başka bir maddeye gerek duymadığınız bir durumsa, aşağıdaki gibi bir şeyin izlenmesi daha açık olabilir (küme parantezlerinin son 2 komutu gruplaması gerekir) ...

[[ $1 == --help ]]  && { echo "$HELP"; exit; }

Birincisi &&ve ||sonuncusu hariç her komutun bir test olduğu operatörler (örneğin [[ ]], parantezlerin içi ), son operatörün beklendiği gibi davranması dışında genellikle aynı şekilde güvenlidir. Son operatör daha çok a thenveya elsecümle gibi davranır .


0

Bununla kafam da karıştı, ancak Bash'in ifadenizi nasıl okuduğunu (sembolleri soldan sağa okuduğu gibi) şöyle düşünüyorum:

  1. Sembol bulundu true. Komutun sonuna gelindiğinde bunun değerlendirilmesi gerekir. Bu noktada, herhangi bir argüman olup olmadığını bilmiyorum. Komutu yürütme arabelleğinde sakla.
  2. Sembol bulundu ||. Önceki komut şimdi tamamlandı, öyleyse değerlendirin. Komut (tampon) yürütülmektedir: true. Değerlendirme sonucu: 0 (örneğin başarı). 0 sonucunu "son değerlendirme" kaydında saklayın. Şimdi sembolün ||kendisini düşünün . Bu, son değerlendirmenin sıfır olmayan sonucuna bağlıdır. "Son değerlendirme" kontrol edildi ve 0 bulundu. 0 sıfır olmadığından, aşağıdaki komutun değerlendirilmesi gerekmez.
  3. Sembol bulundu echo. Bu sembolü görmezden gelebilir, çünkü aşağıdaki komutun değerlendirilmesine gerek yoktu.
  4. Sembol bulundu aaa. Bu, echo(3) komutunun bir argümanıdır , ancak echo(3) değerlendirilmeye ihtiyaç duymadığından ihmal edilebilir.
  5. Sembol bulundu &&. Bu, son değerlendirmenin sonucunun sıfır olmasına bağlıdır. "Son değerlendirme" kontrol edildi ve 0 bulundu. 0 sıfır olduğundan, aşağıdaki komutun değerlendirilmesi gerekir.
  6. Sembol bulundu echo. Aşağıdaki komutun değerlendirilmesi gerektiğinden, komutun sonuna gelindiğinde bu komutun değerlendirilmesi gerekir. Komutu yürütme arabelleğinde sakla.
  7. Sembol bulundu bbb. Bu komut echo(6) bir argümandır . Değerlendirilmesi echogerektiğinden, bbbyürütme arabelleğine ekleyin .
  8. Satırın sonuna ulaştı. Önceki komut şimdi tamamlandı ve değerlendirilmesi gerekiyordu. Komut (tampon) yürütülmektedir: echo bbb. Değerlendirme sonucu: 0 (örneğin başarı). 0 sonucunu "son değerlendirme" kaydında saklayın.

Ve elbette, son adım bbbkonsola yankılanmaya neden olur .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.