web-dev-qa-db-ja.com

「false」をパイピングするとゼロ以外の結果コードが返されない

私はこれらを理解しています:

true;   echo "$?"      # 0
false;  echo "$?"      # 1
true  | echo "$?"      # 0

しかし、これではありません:

false | echo "$?"      # 0

...なぜ表示されないのですか1

そして、どうすればパイプで障害を強制し、1その後?

7
lonix

true | echo "$?"false | echo "$?"の両方の結果は誤解を招く可能性があります。 "$?"の内容が設定されますbeforeコマンドfalseをコマンドechoにパイプします。

これらの行を実行するために、bashはコマンドのパイプラインをセットアップします。パイプラインがセットアップされ、コマンドが並行して開始されます。だからあなたの例では:

true;   echo "$?"      # 0
false;  echo "$?"      # 1
true  | echo "$?"      # 0

と同じです:

true   
echo "$?"      # 0
false  
echo "$?"      # 1
echo "$?"      # 0

trueecho $?の前に実行されず、同時に実行されます。

15
Philip Couling

_false | echo $?_では、_$?_はfalseの終了ステータスではありません。これは、_$?_が10進数に展開されるためですexit =最新のステータスパイプライン[1]、最新のステータスではないコマンドサブシェルまたは子プロセス。 _false | echo $?_では、falseはパイプラインではなく、パイプラインの一部です。 _false;_が含まれていない場合でも、_|_のような単純なcompleteコマンドはパイプラインです。

_set -o pipefail_がオンであり、_false | echo $?_の終了ステータスがfalseの終了ステータスであると仮定すると、_$?_も現在のパイプラインのステータスを終了できませんでした。パイプラインは、エコーするまでにまだ終了していません。

パイプラインの両側(常に並行して実行される)が開始または終了される順序、または_echo $?_は実際には子プロセスまたはサブシェルで実行されます。

FWIW、サブシェルの場合、変数とその他のパラメーターはalwaysが現在のサブシェルのコンテキストで展開されます:_false | echo $?_がパイプラインの両側に別々のプロセスをフォークすることで実装されている場合(これはすべてのシェルではなくbashの場合)、_$?_は子プロセスで展開されますafterfork()、そしておそらくパイプラインの左側が出た後[2]。

そして、どうすればパイプで障害を強制し、その後1を取得できますか?

_set -o pipefail_を使用します。これは、bash、zsh、kshでサポートされており、標準の 将来のバージョン に含める必要があります。しかし、上記で説明したように、これは_$?_ afterの値にのみ影響します。パイプライン自体ではなく、パイプラインが終了しました。


[1] 2.5.2特殊パラメータ SUSv4標準。このコンテキストでコマンド置換がパイプラインと見なされるかどうかは、シェルに依存します。ほとんどの歴史的および現在のシェルでは_:; echo `exit 13` $?_は_13_を出力しますが、一部の(ダッシュ、yash、またはpdkshから派生した)シェルでは_0_を出力します。

[2]これを理解するのに役立つ単純なbashの例は_echo $BASHPID >&2 | echo $BASHPID >&2 | echo $BASHPID >&2_です。 BASHPID変数はnotであり、3つの子プロセスが設定されて実行される前に展開されます。 _$?_のような特別なパラメーターは、この点で特別ではありません。他の変数と同様に展開されます。

6
mosvy

これは、パイプの両方の部分が並行して実行されるため、falseコマンドがまだ完了していない(そして$?echoコマンドがすでに印刷を開始している場合$?、これはpreviousコマンドの結果です。あなたの場合、おそらく最後に実行されたecho(そしておそらくそれは成功したでしょう)。

3
AdminBee