Bashで論理演算子の優先順位がどのように機能するかを理解しようとしています。たとえば、次のコマンドは何もエコーすることを期待していません。
true || echo aaa && echo bbb
ところが私の期待とは違ってbbb
印刷された。
誰かがbashで複合演算子&&
と演算子を理解する方法を説明できますか?||
答え1
多くのコンピュータ言語で同じ優先順位を持つ演算子は次のとおりです。左派労組。すなわち、グループ化構造がない場合には、一番左の演算を先に行う。すごい例外なくこのルールに。
Bashと他のシェルでは優先順位が同じで&&
あるため、これが重要です。||
これは一般的&&
に||
。
したがって、あなたの例では、||
一番左の操作()が最初に行われます。
true || echo aaa
true
明らかに正確であるため、||
演算子は短絡され、期待echo aaa
どおりに評価する必要なしに文全体が正しいと見なされます。これで一番右にある作業だけが残りました。
(...) && echo bbb
最初のタスクはtrue(つまり、終了ステータスが0)と評価されるため、次のタスクを実行するのと同じです。
true && echo bbb
したがって、&&
短絡が発生しないため、bbb
エコーが表示されます。
あなたは同じ行動をします
false && echo aaa || echo bbb
コメントに基づいてコメントを付ける
左の関連ルールは次のとおりです。ただ両方のオペレータ同じ優先事項
[[...]]
orなどのキーワードでこれらの演算子を使用する場合、またはorコマンドの引数としてand演算子を((...))
使用する場合は、そうではありません。この場合、ANDはORよりも優先されます。これを明確に説明してくれたStephane Chazelasに感謝します。-o
-a
test
[
&&
-a
||
-o
そうだCからCに似た言語は、これより
&&
優先順位が高い||
ため、おそらく元の構成が次のように動作すると予想しています。true || (echo aaa && echo bbb).
ただし、両方の演算子の優先順位が同じBashの場合はそうではありません。これがBashが式を解析するために左の関連ルールを使用する理由です。この点を提起したケビンのコメントありがとうございます。
- このような状況もあるかもしれません。全3式が評価されます。最初のコマンドがゼロ以外の終了状態を返す場合、ショートせずに
||
2番目のコマンドで実行が続行されます。 2 番目のコマンドが 0 終了状態に戻った場合、段落も発生せず、3 番目の&&
コマンドが実行されます。この点を提起したIgnacio Vazquez-Abramsのコメントありがとうございます。
答え2
状況に応じて複数の項目を依存させるには、次のようにグループ化します。
true || { echo aaa && echo bbb; }
その間何も印刷しない
true && { echo aaa && echo bbb; }
2つの文字列を印刷します。
このようなことが起こった理由は、ヨセフが想像していたよりもはるかに簡単でした。||
Bashが何をしているのか覚えておいてください&&
。前のコマンドの戻り状態に関するものです。元のコマンドを文字通り表示する方法は次のとおりです。
( 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
答え3
&&
そして||
オペレーターいいえ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?)
両方とも偽の理由そしてTRUEはエコーされましたか?
ここで何が起こっているのかは、私たちがそれを実現することはできず、オーバーロードされた演算&&
子であり、ここにあるANDおよびOR(条件付き実行)リストと条件付きテスト角括弧内で異なる||
動作をすることです。[[ ]]
bashのマンページから(編集済み)...
リスト
リストは、&、&&、または ││ 演算子の 1 つで区切られ、オプションで;、&、または or のいずれかで終わる 1 つ以上のパイプシーケンスです。これらのリスト演算子の中で、&&と│は同じ優先順位を持ち、次に同じ優先順位を持つ&があります。
コマンドを区別するために、セミコロンの代わりに一連の1つ以上の改行がリストに表示されます。
コマンドが制御演算子&で終わると、シェルはサブシェルのバックグラウンドでコマンドを実行します。シェルは、コマンドが完了するのを待たずに状態0を返します。 ;で区切られたコマンドは順次実行されます。シェルは、各コマンドが順番に終了するのを待ちます。戻り状態は、最後に実行されたコマンドの終了状態です。
ANDリストとORリストは、それぞれ&&および│制御演算子で区切られたいくつかのパイプの1つのシーケンスです。 AND リストと OR リストは左の関連付けで実行されます。
ANDリストの形式は次のとおりです。Command2
command1 && command2
は、command1が終了ステータス0を返した場合にのみ実行されます。ORリストの形式は次のとおりです。Command2
command1 ││ command2
は、command1が0以外の終了ステータスを返した場合にのみ実行されます。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"
複数を使用する場合やコマンド・リストで使用する場合、&&
論理||
エラーが発生するリスクはかなり高くなります。
提案
コマンドリストの単一のORは期待&&
どおりに動作するため、非常に安全です。||
else句が必要ない場合は、以下をより明確に従うことができます(最後の2つのコマンドをグループ化するには中括弧が必要です)...
[[ $1 == --help ]] && { echo "$HELP"; exit; }
最後を除く各コマンドがテストである複数の演算子&&
(||
角括弧内など[[ ]]
)も、最後の演算子を除くすべての演算子が期待どおりに機能するため、通常安全です。最後の演算子はthen
orelse
句と同様に機能します。
答え4
私もこれについて混乱しています。しかし、Bashがステートメントを読む方法についての私の見解は次のとおりです(記号を左から右に読むので):
- 記号を見つけます
true
。コマンドの終わりに達したら、それを評価する必要があります。現時点では議論があるかどうかはわかりません。実行バッファにコマンドを保存します。 - 記号が見つかりました
||
。これで前のコマンドが完了しました。評価してみてください。実行中のコマンド(バッファ):true
評価結果:0(つまり成功)。結果0を「最後の評価」レジスタに保存します。それでは、シンボル||
自体を考えてみましょう。これは、最後の評価結果がゼロでないかどうかによって異なります。 「最後の評価」レジスタを確認した結果、0が見つかりました。 0 はゼロ以外の値ではないため、次のコマンドを評価する必要はありません。 - 記号が見つかりました
echo
。次のコマンドを評価する必要がないため、シンボルを無視できます。 - シンボルの検索
aaa
これはコマンド (3) の引数ですが、echo
(echo
3) を評価する必要がないので無視できます。 - 記号を見つけます
&&
。これは、最後の評価結果がゼロかどうかによって異なります。 「最後の評価」レジスタを確認した結果、0が見つかりました。 0 は 0 なので、次のコマンドを評価する必要があります。 - 記号を見つけます
echo
。コマンドの終わりに達すると、次のコマンドを評価する必要があるため、コマンドを評価する必要があります。実行バッファにコマンドを保存します。 - 記号の検索
bbb
これはecho
コマンド(6)の引数です。echo
評価が必要なので、bbb
実行バッファに追加されます。 - 行の終わりに達しました。これで、以前の注文が完了し、評価が必要です。実行中のコマンド(バッファ):
echo bbb
評価結果:0(つまり成功)。結果0を「最後の評価」レジスタに保存します。
もちろん、最後のステップでは、bbb
コンソールにエコーが発生します。