シェルの論理演算子&&、||の優先順位


126

論理演算子の優先順位がbashでどのように機能するかを理解しようとしています。たとえば、次のコマンドは何もエコーしないと予想していました。

true || echo aaa && echo bbb

しかし、私の期待に反して、bbb印刷されます。

誰かが説明することができますか、bashの複合演算子&&||演算子をどのように理解できますか?

回答:


127

多くのコンピューター言語では、同じ優先順位を持つ演算子は左結合です。つまり、グループ化構造がない場合、左端の操作が最初に実行されます。Bashはこのルールの例外ではありません

Bashでは同じ優先順位&&||持っているため、これは重要です。

あなたの例で起こることは、左端の操作(||)が最初に実行されることです:

true || echo aaa

以来true、明らかに事実である、||オペレータ短絡や文全体を評価する必要なしに、真とみなされecho aaaますが、期待通り。これで、右端の操作を行うことができます。

(...) && echo bbb

最初の操作はtrueと評価された(つまり、終了ステータスが0だった)ため、実行しているように見えます

true && echo bbb

ので、&&短絡しないので、bbbエコーが表示されます。

あなたは同じ振る舞いを得るでしょう

false && echo aaa || echo bbb

コメントに基づくメモ

  • 両方の演算子の優先順位が同じ場合にのみ、左結合規則に従うことに注意してください。次のようなキーワードと一緒にこれらの演算子を使用する場合は、このケースではありませんか、または使用し、引数として演算子をまたはコマンド。このような場合、AND(または)がOR(または)より優先されます。この点を明確にしてくれたStephane Chazelasのコメントに感謝します。[[...]]((...))-o-atest[&&-a||-o
  • と思われるCにし、C-ような言語&&よりも高い優先順位がある||あなたは、元の構文は次のように動作するように期待する理由はおそらくであるが

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

    ただし、これはBashの場合ではなく、両方の演算子の優先順位が同じであるため、Bashは左結合規則を使用して式を解析します。これを提起してくれたケビンのコメントに感謝します。

  • 3つのすべてが評価される場合もあります。最初のコマンドがゼロ以外の終了ステータスを返した場合、||ショートすることはなく、2番目のコマンドを実行し続けます。2番目のコマンドがゼロの終了ステータスで戻った場合、&&同様に短絡することはなく、3番目のコマンドが実行されます。これを提起してくれたIgnacio Vazquez-Abramsのコメントに感謝します。


26
もう少し混乱させてください。in &&||asの演算子cmd1 && cmd2 || cmd3は同じ優先順位を持ちますが、&&in ((...))とin [[...]]は優先順位が||((a || b && c))is ((a || (b && c))))です。同じことがのために行く-a/ -otest/ [及びfind及び&/ |expr
ステファンシャゼル

16
cライクな言語で&&は、より優先順位が高い||ため、OPの予期される動作が発生します。そのような言語に慣れている不注意なユーザーは、bashで同じ優先順位を持っていることに気付かない場合があるため、bashで同じ優先順位を持っていることをより明確に指摘する価値があります。
ケビン

また、3つのコマンドすべてを実行できる状況、つまり真ん中のコマンドがtrue または falseを返すことができる場合があることに注意してください。
イグナシオバスケス-エイブラムス

@Kevin編集済みの回答を確認してください。
ジョセフR.

1
今日、私にとって、「bash演算子の優先順位」の上位ヒットはtldp.org/LDP/abs/html/opprecedence.htmlです...これは、&&の優先順位が||より高いと主張しています。まだ@JosephR。事実上および法的にも明らかに正しい。ダッシュは同じように動作し、pubs.opengroup.org / onlinepubs / 009695399 / utilities /…で「優先順位」を検索します。これはPOSIXの要件であるため、それに依存することができます。バグ報告で私が見つけたのは、作者のメールアドレスだけでした。このメールアドレスは、過去6年間で確実にスパムとして殺されています。私は...とにかく頑張ります
マーティンDorey

62

条件に応じて複数のことをしたい場合は、それらをグループ化します。

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

それは何も印刷しませんが、

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

両方の文字列を出力します。


これが起こる理由は、ジョセフが考えているよりもずっと簡単です。バッシュはで何をするかを覚えておいてください||&&。それはすべて、前のコマンドの戻りステータスに関するものです。生のコマンドを文字通り見る方法は次のとおりです。

( true || echo aaa ) && echo bbb

最初のコマンド(true || echo aaa)はで終了してい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の意図した結果を達成するため。あなたが置いた括弧(true || echo aaa) && echo bbbはまさに私が作っているものであることに注意してください。
ジョセフR.

1
世界のこちら側で@Oliに会えてうれしいです。
Braiam

7
;最後のセミカラムに注意してください。これがないと機能しません!
セルジュStroobandt

2
最後のコマンド(たとえばecho bbb;最初の例)の後に末尾のセミコロンがなかったため、これで問題が発生していました。私がそれを見逃していたことがわかったら、この答えはまさに私が探していたものでした。+1は、私が望んでいたことを達成する方法を理解するのを助けてくれました!
ドクターJ

5
混乱し(...){...}表記されている人のために読む価値がある:コマンドグループマニュアル
アンタラ

16

演算子はありませんされたif-then-elseの正確なインライン交換。ただし、慎重に使用すれば、ほぼ同じことを達成できます。&&||

単一のテストは簡単で明確です...

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

ただし、複数のテストを追加しようとすると、予期しない結果が生じる可能性があります...

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

なぜFALSE TRUEの両方がエコーされるのですか?

何ここで起こっていることは、我々はそれを実現していませんでしたということである&&||の条件付きテスト括弧内の異なる動作をオペレータオーバーロードされ[[ ]]、彼らは我々がここにあるANDとOR(条件実行)リストで行うよりもを。

bashのマンページから(編集済み)...

リスト

リストは、演算子;、&、&&、または│|のいずれかで区切られた1つ以上のパイプラインのシーケンスであり、オプションで;、&、または。のいずれかで終了します。これらのリスト演算子のうち、&&と││の優先順位は等しく、その後に;が続きます。および&、同等の優先順位があります。

コマンドを区切るために、1つ以上の改行のシーケンスがセミコロンではなくリストに表示される場合があります。

コマンドが制御演算子&によって終了された場合、シェルはサブシェルのバックグラウンドでコマンドを実行します。シェルはコマンドが終了するまで待機せず、戻りステータスは0です。順次実行されます。シェルは各コマンドが順番に終了するのを待ちます。戻りステータスは、最後に実行されたコマンドの終了ステータスです。

ANDおよびORリストは、それぞれ&&および││制御演算子で区切られた1つ以上のパイプラインのシーケンスです。ANDおよびORリストは左結合で実行されます。

ANDリストの形式は...です
command1 && command2
。command2は、command1が終了ステータス0を返す場合にのみ実行されます。

ORリストの形式は... ...
command1 ││ command2
command2が実行されるのは、command1がゼロ以外の終了ステータスを返す場合のみです。

ANDおよびORリストの戻りステータスは、リストで最後に実行されたコマンドの終了ステータスです。

最後の例に戻ります...

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

はい。それが正しい場合、最後から2番目の例が何もエコーしないのはなぜですか?

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

複数のコマンドを使用し&&たり||、コマンドリストで使用したりすると、論理エラーのリスクが非常に高くなります。

推奨事項

単一&&または||コマンドリストは期待どおりに機能するため、非常に安全です。else節を必要としない状況の場合、次のようなことがわかりやすくなります(最後の2つのコマンドをグループ化するには中括弧が必要です)...

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

複数の&&and ||演算子は、最後を除く各コマンドがテスト(つまり[[ ]]、かっこ内)である場合、通常、最後の演算子を除くすべてが期待どおりに動作するため安全です。最後の演算子は、thenor else句のように機能します。


0

私もこれに困惑しましたが、Bashがあなたの声明を読む方法について考えています(左から右に記号を読むように):

  1. シンボルが見つかりましたtrue。コマンドの最後に到達したら、これを評価する必要があります。この時点で、引数があるかどうかはわかりません。コマンドを実行バッファに保存します。
  2. シンボルが見つかりました||。前のコマンドは完了しましたので、評価してください。コマンド(バッファ)が実行されています:true。評価の結果:0(つまり成功)。結果0を「最後の評価」レジスタに保存します。次に、シンボル||自体を検討します。これは、最後の評価がゼロ以外の結果に依存します。「最後の評価」レジスタをチェックし、0を見つけました。0は非ゼロではないため、次のコマンドを評価する必要はありません。
  3. シンボルが見つかりましたecho。次のコマンドを評価する必要がないため、このシンボルを無視できます。
  4. シンボルが見つかりましたaaa。これはコマンドecho(3)の引数ですが、echo(3)を評価する必要がないため、無視できます。
  5. シンボルが見つかりました&&。これは、最後の評価の結果がゼロに依存します。「最後の評価」レジスタをチェックし、0を見つけました。0はゼロなので、次のコマンドを評価する必要があります。
  6. シンボルが見つかりましたecho。次のコマンドを評価する必要があったため、コマンドの最後に到達したら、このコマンドを評価する必要があります。コマンドを実行バッファに保存します。
  7. シンボルが見つかりましたbbb。これはコマンドecho(6)の引数です。以来echo評価する必要がなかった、追加bbb実行バッファへ。
  8. 行末に達しました。前のコマンドは現在完了しており、評価する必要がありました。コマンド(バッファ)が実行されています:echo bbb。評価の結果:0(つまり成功)。結果0を「最後の評価」レジスタに保存します。

そしてもちろん、最後のステップbbbはコンソールにエコーされます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.