POSIX sh条件における論理OR評価の予期しない結果

POSIX sh条件における論理OR評価の予期しない結果

次のようなifステートメントがあるとします。

if [ $(false) ]; then
    echo "?"
fi

これにより、「?」は印刷されません(条件が偽です)。ところで、次のような場合には「?!」が出力されますが、なぜでしょうか。

if [ $(false) -o $(false) ]; then
    echo "?!"
fi

答え1

$(false)falseと評価されず、空の文字列を生成します。引用がないので、

if [ $(false) ]; then

次のように評価

if [ ]; then

[空の式は偽なので、これは偽です。

if [ $(false) -o $(false) ]; then

次のように評価

if [ -o ]; then

これいいえ-o演算子を使用すると、-o単一の文字列を含む式で評価されます。[これらの式はtrueなので、thenステートメントの一部がif実行されます。

バラよりPOSIX仕様test、特に:

演算子の優先順位と生成される戻り値を決定するために使用されるアルゴリズムは、テストする引数の数に基づいています。 (ただし、「[...]」形式を使用する場合は、最後のパラメータ<右角括弧>をアルゴリズムに含めないでください。)

次のリストでは、$ 1、$ 2、$ 3、および$ 4はテストするパラメータを示しています。

引数 0 個:
false(1) を終了します。
パラメータ1:
$ 1がnullでない場合はtrue(0)を返し、そうでない場合はfalseを返します。

test演算子は、2つ以上の引数が指定されている場合にのみ考慮されます。

コマンドの終了ステータスを条件として使用するには、コマンドの置き換えまたは次の項目に入れないでください[ ]

if false; then

そして

if false || false; then

また参考にしてくださいtest-a演算子は-o廃止され、信頼できません。;シェルの&&AND||演算子を使用する必要があります。例えば

if [ "$a" = b ] || [ "$a" = c ]; then

答え2

素晴らしい答えで指摘した内容を繰り返しません。寄稿者: @StephenKittそしてこんにちはここでは、書き込みの意味にのみ焦点を当てますif [ $(cmd) ]; then...

まずこれでは[ないことを知っておいてください。POSIXsh演算子とも呼ばれる別のユーティリティです。testここで、文法の //// 文のif部分に使用されます。ifthenelifelsefish言語

その使命は、引数を条件式で評価することです。たとえば、、、、、およびを[パラメータとして受け取るか、、、、およびを受け取ると、次のように解釈さaれるため、false/失敗した終了ステータスを返します。=b]testa=b「'a'と'b'は同じ文字列ですか?」条件式

[通常、//ステートメントの一部として使用されるか、if//または//ステートメント内の唯一のコマンドとして使用されますが、これらのステートメントである必要もなく、単一のコマンドを呼び出す必要もありません。ifthenelifelsefiwhiledodoneuntildodone[

実行とは、を使用して結果を拡張し、コマンドを別の引数として呼び出すことを[ $(cmd) ]意味します。その後、これらのパラメータは条件式として解釈され、結果に応じてtrueまたはfalseを返します(式が理解できない場合はfalse)。[[$(cmd)][

POSIX shは$(cmd)すべての末尾の改行文字を削除して標準出力に拡張しcmd、引用符がなくリストコンテキストにあるため、IFS分割後にワイルドカードが適用されます(その出力にNUL文字がある場合の動作は指定されません)。

だからif [ $(cmd) ]、またはtest $(cmd)何の意味もありません。

それは質問をするのと同じです:「分割+グローブされると、出力がcmdtrueと評価される有効な条件式を構成しますか?」

たとえば、cmd出力合計に偶然含まれていて、a,*現在の作業ディレクトリに2つのファイル(1つは名前が付けられ、もう1つは名前付き)が含まれている場合、そのファイルは引数として、、、、を使用して呼び出されます。幸い、これは有効な条件式ですが、falseを返す条件式はそうではありません。$IFS,=b[ $(cmd) ][[a=b]ab

$ cd "$(mktemp -d)"
$ touch = b
$ cmd() { echo 'a,*'; }
$ IFS=,
$ set -o xtrace
$ if [ $(cmd) ]; then echo yes; else echo no; fi
++ cmd
++ echo 'a,*'
+ '[' a = b ']'
+ echo no
no

[ "$(cmd)" ]より意味があります。 Quoteコマンド置換は、NULを出力するときに未指定のまま残っている末尾の改行文字の削除を防ぎませんがcmd、分割+globおよびnullの削除を防ぎます。したがって、[3つの引数が常に渡されます。、、[末尾の改行が削除された出力、および。横には、そのパラメーターが空の文字列でない場合にのみtrueを返すパラメーターがあります。これはと同じです。cmd][][[ -n "$(cmd)" ]

だからそれは次のように言うのと同じです:"cmd改行文字(またはNUL)以外の文字を1つ以上出力してください。。テキストを出力するコマンドの場合(テキストが行で構成され、NULを含まないことが保証されています)cmd「空白以外の行を1つ以上出力しますか?」

短い出力を除くと、出力全体を読み取って転送する前にメモリに保存されるため、これは非効率的な方法でこれを行います[

if cmd | grep -q .; then...

grep終了するので、より効率的です。本物少なくとも1つの文字を含む行を見つけた場合。

その場合、最終的にシェルがクラッシュしますcmdyesif [ "$(cmd)" ]十分な保存エラーが発生すると、if cmd | grep -q .yesはすぐに出力されます(yesSIGPIPE信号で終了します)。

成功するかどうかをテストするには、cmd出力の有無にかかわらず、次のようにします。

if cmd; then
  echo cmd succeeded
else
  echo cmd failed
fi

if $(cmd); then話すべきもう一つのコード例です。しかし、cmd存在する例は少し異なりますfalse

繰り返しますが、末尾の$(cmd)改行文字は削除され、分割+グローブの影響を受けます。ただし、今回は生成された単語が[/testユーティリティに渡されず、これらの単語のうち最初の単語(存在する場合)が実行されるコマンドとして処理され、すべての単語が引数として渡されます。

したがって、cmd出力echo:hello:world$IFSインクルードが引数としておよび引数として実行され、stdoutに正常に書き込むことができると仮定すると、true :/成功を返すため、その部分が実行されます。echoechohelloworldecho"hello world\n"then

cmd出力が生成されない場合、または改行とIFS空白文字のみが生成されると、単語はまったく生成されないため、$(cmd)他のコマンドは実行されず、この時点ではcmdステートメントの終了状態が重要ですif

存在する:

if $(false); then
  echo yes
else
  echo no
fi

no出力がfalse生成されなかったため、コマンドが実行されなかったために出力され、終了ステータスによってそのセクションが実行されるかどうかがfalse決まります。else

存在する:

if $(echo true; false); then
  echo yes
else
  echo no
fi

、または文字を含まないと$IFS想定され、最終的に実行され、終了ステータスがステートメント内の分岐を決定します。truetrueif

関連情報