'が別の

2つのプロセスにパイプされているプロセスの終了ステータスを取得しますfooおよびbar、パイプで接続:

$ foo | bar 

barは常に0を終了します。 fooの終了コードに興味があります。それを取得する方法はありますか?

コメント

回答

bashzshには配列変数がありますこれは、シェルによって実行された最後のパイプラインの各要素(コマンド)の終了ステータスを保持します。

bashを使用している場合、配列は

(大文字と小文字が区別されます!)および配列インデックスはゼロから始まります:

$ false | true $ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}" 1 0 

zsh、配列はpipestatusと呼ばれ(大文字と小文字が区別されます)、配列のインデックスは1から始まります:

$ false | true $ echo "${pipestatus[1]} ${pipestatus[2]}" 1 0 

値を失わない方法で関数内でそれらを組み合わせるには:

$ false | true $ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$? $ echo $retval_bash $retval_zsh $retval_final 1 0 

上記を

またはzshを実行すると、同じ結果が得られます。retval_bashretval_zshのいずれか1つのみが設定されます。もう一方は空白になります。これにより、関数をreturn $retval_bash $retval_zshで終了できます(引用符がないことに注意してください)。

コメント

  • そしてzshのpipestatus。残念ながら、他のシェルにはこの機能がありません。’この機能はありません。
  • 注:zshの配列は、直感に反してインデックス1から始まるため、’ s echo "$pipestatus[1]" "$pipestatus[2]"
  • パイプライン全体を次のように確認できます:if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
  • @JanHudec:おそらく、私の答えの最初の5つの単語を読む必要があります。また、質問がPOSIXのみの回答を要求した場所を親切に指摘してください。
  • @JanHudec:POSIXのタグも付けられていません。なぜ答えはPOSIXでなければならないと思いますか?指定されていないので、適切な回答を提供しました。私の答えには間違いはありません。さらに、他のケースに対処するための複数の答えがあります。

答え

ありますこれを行う3つの一般的な方法は次のとおりです。

Pipefail

最初の方法は、pipefailオプション(kshzshまたはbash)。これは最も簡単で、基本的に終了ステータス$?を最後のプログラムの終了コードに設定してゼロ以外(またはすべてが正常に終了した場合はゼロ)で終了します。

$ false | true; echo $? 0 $ set -o pipefail $ false | true; echo $? 1 

$ PIPESTATUS

Bashには、$PIPESTATUS$pipestatus in zsh)これには、最後のパイプラインのすべてのプログラムの終了ステータスが含まれます。

$ true | true; echo "${PIPESTATUS[@]}" 0 0 $ false | true; echo "${PIPESTATUS[@]}" 1 0 $ false | true; echo "${PIPESTATUS[0]}" 1 $ true | false; echo "${PIPESTATUS[@]}" 0 1 

3番目のコマンド例を使用して、必要なパイプラインの特定の値を取得できます。

個別の実行

これは最も扱いにくいソリューションです。各コマンドを個別に実行し、ステータスをキャプチャします

$ OUTPUT="$(echo foo)" $ STATUS_ECHO="$?" $ printf "%s" "$OUTPUT" | grep -iq "bar" $ STATUS_GREP="$?" $ echo "$STATUS_ECHO $STATUS_GREP" 0 1 

コメント

  • くそっ! PIPESTATUSについて投稿するつもりでした。
  • 参考までに、このSOの質問で説明されている他のテクニックがいくつかあります: stackoverflow.com/questions/1221833/ …
  • @Patrickパイプステータスソリューションはbashで機能しますが、kshスクリプトを使用する場合は、pipestatusに似たものを見つけることができると思いますか? 、(pipestatusがkshでサポートされていないことがわかります)
  • @yael ‘はkshを使用しません。ただし、’のマンページを一目見ただけでは、’は$PIPESTATUSをサポートしていません。または同様のもの。ただし、pipefailオプションはサポートされています。
  • 失敗したコマンドのステータスをここで取得できるため、pipefailを使用することにしました:LOG=$(failed_command | successful_command)

回答

このソリューションは、bash固有の機能や一時ファイルを使用しなくても機能します。ボーナス:最終的に、終了ステータスは実際には終了ステータスであり、ファイル内の文字列ではありません。

状況:

someprog | filter 

あなたsomeprogからの終了ステータスとfilterからの出力が必要です。

これが私の解決策です:

((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1 

このコンストラクトの結果は、filterからのstdoutであり、。


この構成は、サブシェル(...)の代わりに、単純なコマンドグループ{...}でも機能します。サブシェルには、パフォーマンスコストなど、いくつかの影響がありますが、ここでは必要ありません。詳細については、細かいbashのマニュアルをお読みください: https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1 

残念ながら、bash文法では、中括弧にスペースとセミコロンが必要であるため、構成がはるかに広くなります。

このテキストの残りの部分では、サブシェルバリアントを使用します。


someprogおよびfilter

someprog() { echo "line1" echo "line2" echo "line3" return 42 } filter() { while read line; do echo "filtered $line" done } ((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1 echo $? 

出力例:

filtered line1 filtered line2 filtered line3 42 

注:子プロセスは、開いているファイル記述子を親から継承します。つまり、someprogは開いているファイル記述子3と4を継承します。someprogがファイル記述子3に書き込むと、それが終了ステータスになります。 readは1回しか読み取らないため、実際の終了ステータスは無視されます。

someprogが次のように書き込む可能性がある場合ファイル記述子3または4にアクセスする場合は、someprogを呼び出す前にファイル記述子を閉じるのが最善です。

(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1 

someprogの前のexec 3>&- 4>&-は、someprogを実行する前にファイル記述子を閉じます。 e22699ffb0 “>

これらのファイル記述子は単に存在しません。

次のように記述することもできます:someprog 3>&- 4>&-


構成のステップバイステップの説明:

( ( ( ( someprog; #part6 echo $? >&3 #part5 ) | filter >&4 #part4 ) 3>&1 #part3 ) | (read xs; exit $xs) #part2 ) 4>&1 #part1 

下から上へ:

  1. ファイル記述子を使用してサブシェルが作成されます4stdoutにリダイレクトされました。これは、サブシェルのファイル記述子4に出力されるものはすべて、コンストラクト全体の標準出力になることを意味します。
  2. パイプが作成され、左側のコマンド(#part3)とright(#part2)が実行されます。 exit $xsはパイプの最後のコマンドでもあり、stdinからの文字列がコンストラクト全体の終了ステータスになることを意味します。
  3. サブシェルはファイルで作成されます記述子3はstdoutにリダイレクトされました。これは、このサブシェルのファイル記述子3に出力されるものはすべて、#part2になり、コンストラクト全体の終了ステータスになることを意味します。
  4. Aパイプが作成され、左側(#part5および#part6)と右側(filter >&4のコマンド>)が実行されます。 filterの出力はファイル記述子4にリダイレクトされます。#part1では、ファイル記述子4がstdoutにリダイレクトされました。これは、filterの出力がコンストラクト全体の標準出力であることを意味します。
  5. #part6からの終了ステータスが出力されます。ファイル記述子3に。#part3で、ファイル記述子3は#part2にリダイレクトされました。これは、#part6からの終了ステータスがコンストラクト全体の最終的な終了ステータスになることを意味します。
  6. someprogは実行されました。終了ステータスは#part5で取得されます。 stdoutは、#part4のパイプによって取得され、filterに転送されます。 filterからの出力は、#part4

コメント h3で説明されているように、順番にstdoutに到達します。 >

  • いいですね。関数の場合、(read; exit $REPLY)
  • (exec 3>&- 4>&-; someprog)someprog 3>&- 4>&-
  • このメソッドは、サブシェルがなくても機能します:{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1

回答

正確に要求したものではありませんが、

#!/bin/bash -o pipefail 

を使用して、パイプが最後のゼロ以外のリターンを返すようにすることができます。

コーディングが少し少ないかもしれません

編集:例

[root@localhost ~]# false | true [root@localhost ~]# echo $? 0 [root@localhost ~]# set -o pipefail [root@localhost ~]# false | true [root@localhost ~]# echo $? 1 

コメント

  • set -o pipefailは、より堅牢である必要があります。誰かがbash foo.shを介してスクリプトを実行した場合。
  • それはどのように機能しますか?例はありますか?
  • -o pipefailはPOSIXにないことに注意してください。
  • これは私のBASH3.2.25( 1)-リリース。 / tmp / ffの上部に#!/bin/bash -o pipefailがあります。エラーは次のとおりです:/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
  • @FelipeAlvarez:一部の環境(Linuxを含む)は’のスペースを解析しませんid = “35034b70b2”>

行は最初の行を超えているため、これは/bin/bash -o pipefail 必要な/bin/bash -o pipefail getopt(または同様の)optargを使用した解析。これはARGV-oの引数として、失敗します。 exec /bin/bash -o pipefail "$@"を実行したラッパー(たとえば、bash-pfを作成し、それを行、それは機能します。参照: en.wikipedia.org/wiki/Shebang_%28Unix%29

回答

可能な場合は、終了コードをfooからbar。たとえば、fooが数字だけの行を生成しないことがわかっている場合は、終了コードを追加するだけです。

{ foo; echo "$?"; } | awk "!/[^0-9]/ {exit($0)} {…}" 

または、fooからの出力に、:

{ foo; echo .; echo "$?"; } | awk "/^\.$/ {getline; exit($0)} {…}" 

これは、bar最後の行を除くすべてで作業し、最後の行を終了コードとして渡します。

barが複雑なパイプラインである場合、その出力は不要です。別のファイル記述子に終了コードを出力することで、その一部をバイパスできます。

exit_codes=$({ { foo; echo foo:"$?" >&3; } | { bar >/dev/null; echo bar:"$?" >&3; } } 3>&1) 

この後$exit_codesは通常foo:X bar:Yですが、barが終了した場合はbar:Y foo:Xになる可能性がありますすべての入力を読む前、または運が悪ければ。最大512バイトのパイプへの書き込みは、すべてのユニスでアトミックであると思います。したがって、foo:$?bar:$?の部分は次のように混在しません。タグ文字列が507バイト未満である限り。

barからの出力をキャプチャする必要がある場合、困難になります。上記の手法を組み合わせて、 barの出力には、終了コードの表示のように見える行が含まれることはありませんが、面倒になります。

output=$(echo; { { foo; echo foo:"$?" >&3; } | { bar | sed "s/^/^/"; echo bar:"$?" >&3; } } 3>&1) nl=" " foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*} bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*} output=$(printf %s "$output" | sed -n "s/^\^//p") 

そしてもちろん、ステータスを保存するために一時ファイルを使用するという簡単なオプションがあります。シンプルですが、本番環境ではシンプルではありません:

  • 複数のスクリプトが同時に実行されている場合、または同じスクリプトが複数の場所でこのメソッドを使用している場合は、次のようにする必要があります。異なる一時ファイル名を使用していることを確認してください。
  • 共有ディレクトリに一時ファイルを安全に作成するのは困難です。多くの場合、/tmpは、スクリプトが確実にファイルを書き込める唯一の場所です。 mktemp を使用します。これはPOSIXではありませんが、現在すべての深刻なユニスで利用できます。
foo_ret_file=$(mktemp -t) { foo; echo "$?" >"$foo_ret_file"; } | bar bar_ret=$? foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file") 

コメント

  • 一時ファイルアプローチを使用する場合、すべての一時ファイルを削除するEXITのトラップを追加することを好みますスクリプトが停止してもゴミが残らないようにする

回答

パイプラインから開始:

foo | bar | baz 

これは、POSIXシェルのみを使用し、一時ファイルを使用しない一般的なソリューションです。

exec 4>&1 error_statuses="`((foo || echo "0:$?" >&3) | (bar || echo "1:$?" >&3) | (baz || echo "2:$?" >&3)) 3>&1 >&4`" exec 4>&- 

$error_statusesには、失敗したプロセスのステータスコードがランダムな順序で含まれ、各ステータスを発行したコマンドを示すインデックスが付いています。

# if "bar" failed, output its status: echo "$error_statuses" | grep "1:" | cut -d: -f2 # test if all commands succeeded: test -z "$error_statuses" # test if the last command succeeded: ! echo "$error_statuses" | grep "2:" >/dev/null 

私のテストでは$error_statusesの周りの引用符に注意してください。それらがないと、grepは、新しい行がスペースに強制されるため、区別できません。

回答

moreutils パッケージがインストールされている場合は、 mispipe ユーティリティを使用して要求どおりに実行できます。

回答

それで、私はlesmanaのような回答を提供したかったのですが、私の方がおそらくもう少し単純で、もう少し有利な純粋なものだと思います- Bourneシェルソリューション:

# You want to pipe command1 through command2: exec 4>&1 exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1` # $exitstatus now has command1"s exit status. 

これは裏返しに説明するのが最も良いと思います– command1は実行され、通常の出力をstdout(ファイル記述子1)に出力します。 、その後、それが完了すると、printfはcommand1の終了コードを実行してそのstdoutに出力しますが、そのstdoutはファイル記述子3にリダイレクトされます。

command1の実行中、そのstdoutはパイプされます。 command2(printfの出力がcommand2に到達することはありません。これは、1ではなくファイル記述子3に送信するためです。 sパイプが読み取るもの)。次に、command2の出力をファイル記述子4にリダイレクトして、ファイル記述子1からも除外されるようにします。ファイル記述子3のprintf出力をに戻すため、ファイル記述子1を少し後で解放する必要があるためです。ファイル記述子1–これは、コマンド置換(バッククォート)がキャプチャし、変数に配置されるものだからです。

最後の魔法は最初のexec 4>&1別のコマンドとして実行しました。ファイル記述子4を外部シェルのstdoutのコピーとして開きます。コマンド置換は、その中のコマンドの観点から標準に書き込まれたものをすべてキャプチャします–ただし、コマンド置換に関する限り、command2の出力はファイル記述子4に送られるため、コマンド置換はそれをキャプチャしません–ただし、コマンド置換から「外れる」と、スクリプトの全体的なファイル記述子1に実質的に移動します。

exec 4>&1には多くの一般的なシェルは、置換を使用している「外部」コマンドで開かれるコマンド置換内のファイル記述子に書き込もうとすると、それを好まないため、別個のコマンドになります。したがって、これは最も簡単な移植可能な方法です。

コマンドの出力が互いに跳躍しているように、技術的ではなく、より遊び心のある方法で見ることができます。command1がcommand2にパイプすると、printfの出力がジャンプします。 command2がそれをキャッチしないように、command 2をオーバーし、コマンド2の出力がジャンプしてthから飛び出します。 eコマンド置換は、printfが置換によってキャプチャされるのにちょうど間に合うように着地し、変数に格納され、command2の出力は、通常のパイプの場合と同様に、標準出力に書き込まれます。

また、私が理解しているように、$?には、変数の割り当て、コマンド置換、および複合コマンドが含まれているため、パイプ内の2番目のコマンドの戻りコードが含まれます。これらはすべて、内部のコマンドの戻りコードに対して事実上透過的であるため、command2の戻りステータスを伝播する必要があります。これは、追加の関数を定義する必要がないため、提案されたものよりもいくらか優れた解決策になると思います。

lesmanaが言及している警告によると、command1はある時点でファイル記述子3または4を使用する可能性があるため、より堅牢にするには、次のようにします。

exec 4>&1 exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1` exec 4>&- 

この例では複合コマンドを使用していますが、サブシェル(ivを使用)を使用していることに注意してください{ }の代わりにid = “478eaad94a”>

も機能しますが、効率が低下する可能性があります。)

コマンドはプロセスからファイル記述子を継承しますこれによりそれらが起動されるため、2行全体がファイル記述子4を継承し、複合コマンドの後に3>&1が続くとファイル記述子3が継承されます。したがって、4>&-は、内部の複合コマンドがファイル記述子4を継承しないようにし、3>&-はファイル記述子3を継承しないようにします。 command1は、「よりクリーンな」より標準的な環境を取得します。内側の4>&-3>&-の横に移動することもできますが、その範囲をできるだけ制限しないのはなぜかと思います。

ファイル記述子3と4を直接使用する頻度はわかりません。ほとんどの場合、プログラムは、その時点で使用されていないファイル記述子を返すsyscallを使用していると思いますが、コードがファイルに書き込むこともあります。記述子3を直接推測します(プログラムがファイル記述子をチェックして開いているかどうかを確認し、開いている場合はそれを使用するか、開いていない場合はそれに応じて異なる動作をすることを想像できます)。したがって、後者を維持するのがおそらく最善です。念頭に置いて、汎用の場合に使用してください。

コメント

  • 面白そうですが、’このコマンドが何を期待するかを完全に理解しておらず、私のコンピューターも’できません。-bash: 3: Bad file descriptorを取得します。
  • @ G-Manそうですね、bashが’が共同で何をしているのかわからないことを忘れ続けています私が通常使用するシェル(busyboxに付属するash)とは異なり、ファイル記述子に混乱します。 ‘ bashを幸せにする回避策を考えたら、お知らせします。それまでの間、’ debianボックスが手元にある場合は、ダッシュで試すことができます。’がbusyboxが手元にある場合は、
  • @ G-Manコマンドが実行することを期待していること、および他のシェルで実行することについては、command1からのリダイレクトstdoutであるため、’コマンド置換に捕らえられませんが、コマンド置換の外に出ると、fd3がstdoutに戻されるため、’は期待どおりにパイプされますcommand2へ。command1が終了すると、printfが起動して終了ステータスを出力します。これは、コマンド置換によって変数に取り込まれます。ここで非常に詳細な内訳: stackoverflow.com/questions/985876/tee-and-exit-status/ … また、あなたのコメントは、それがちょっと侮辱的であることを意図しているかのように読まれましたか?
  • どこから始めましょうか? (1)侮辱されたらごめんなさい。 「面白そう」というのは真剣な意味でした。それと同じくらいコンパクトなものがあなたが期待したようにうまくいったなら、それは素晴らしいことです。それを超えて、私は単純に、あなたのソリューションが何をしているのか理解できなかったと言っていました。私は(Linuxが存在する前から)長い間Unixで作業/遊んでいますが、何かがわからない場合は、多分という危険信号です。 i>他の人もそれを理解せず、もっと説明が必要だと(IMNSHO)。 …(続き)
  • (続き)…あなた以来「…考えたい…[あなた]は約Stack Exchangeの目的は、コマンドを作成するサービスではなく、些細な質問に対して何千もの1回限りのソリューションを生み出すことではないことを覚えておく必要があります。むしろ、自分の問題を解決する方法を人々に教えるためです。そして、そのためには、「平均的な人」が理解できるように、十分に説明する必要があるかもしれません。優れた説明の例については、レスマナの回答をご覧ください。 …(続き)

回答

上記のlesmanaのソリューションは、オーバーヘッドなしで実行することもできます。代わりに{ .. }を使用してネストされたサブプロセスを開始します(この形式のグループ化されたコマンドは常にセミコロンで終了する必要があることに注意してください)。

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1 

ダッシュバージョン0.5.5とbashバージョン3.2.25および4.2.42でこの構成をチェックしたので、一部のシェルが{ .. }グループ化、それはまだPOSIXに準拠しています。

コメント

  • これは、ほとんどのシェルで非常にうまく機能しますI ‘ NetBSD sh、pdksh、mksh、dash、bashなどで試してみましたが、’ AT & T Ksh(93s +、93u +)またはzsh(4.3.9、5.2)、kshのset -o pipefailまたは任意の数のwaitコマンドのいずれか。部分的にはそうかもしれないと思います。少なくとも、サブシェルの使用に固執するかのように、kshの解析の問題になります。それは正常に機能しますが、ifを使用しても、kshのサブシェルバリアントを選択しますが、複合コマンドはそのままにします。他の人にとっては失敗します。

回答

以下は、@ Patrikの回答へのアドオンを意味します。一般的な解決策の1つを使用できない場合に備えて。

この回答は、次のことを前提としています。

  • またはset -o pipefail
  • 並列実行にパイプを使用するため、一時ファイルは使用しません。
  • あなた突然の停電などによってスクリプトが中断された場合でも、混乱が生じないようにしてください。
  • このソリューションは、比較的簡単に理解でき、読みやすくなっているはずです。
  • そうします。追加のサブシェルを導入したくない。
  • 既存のファイル記述子をいじることはできないため、stdin / out / errに触れないでください(方法一時的に新しいものを導入することができます)

追加の前提条件。すべてを取り除くことはできますが、これはレシピを覆い隠しすぎるため、ここでは取り上げません。

  • 知りたいのは、PIPEのすべてのコマンドの終了コードが0であることだけです。
  • 追加のサイドバンド情報は必要ありません。
  • シェルはすべてのパイプコマンドが返されるのを待ちます。

変更前:foo | bar | bazただし、これは最後のコマンドの終了コード(baz)のみを返します

必要:パイプ内のコマンドのいずれかが失敗した場合、$?0(true)にすることはできません

変更後:

TMPRESULTS="`mktemp`" { rm -f "$TMPRESULTS" { foo || echo $? >&9; } | { bar || echo $? >&9; } | { baz || echo $? >&9; } #wait ! read TMPRESULTS <&8 } 9>>"$TMPRESULTS" 8<"$TMPRESULTS" # $? now is 0 only if all commands had exit code 0 

説明:

  • tempfileはmktemp。これにより、通常、すぐに/tmp
  • にファイルが作成されます。

  • この一時ファイルは、書き込み用にFD 9にリダイレクトされ、読み取り用にFD8にリダイレクトされます
  • tempfileはすぐに削除されます。ただし、両方のFDが存在しなくなるまで、開いたままになります。
  • これで、パイプが開始されます。エラーが発生した場合にのみ、各ステップがFD9に追加されます。
  • kshにはwaitが必要です。 ksh elseは、すべてのパイプコマンドが終了するのを待たないためです。ただし、バックグラウンドタスクが存在する場合は不要な副作用があることに注意してください。デフォルトでコメントアウトしました。待機に問題がない場合は、コメントを入力できます。
  • その後、ファイルの内容が読み込まれます。空の場合(すべてが機能したため)read falseを返すため、trueはエラーを示します

これはプラグインの代替として使用できます単一のコマンドであり、以下のみが必要です。

  • 未使用のFD9および8
  • 一時ファイルの名前を保持する単一の環境変数
  • そしてこれレシピは、IOリダイレクトを可能にする、あらゆるシェルに適合させることができます
  • また、プラットフォームに依存せず、/proc/fd/N
  • バグ:

    /tmpのスペースが不足した場合に備えて、このスクリプトにはバグがあります。この人工的なケースに対する保護も必要な場合は、次のように実行できますが、これには欠点があります。0000の数は、パイプなので、少し複雑になります:

TMPRESULTS="`mktemp`" { rm -f "$TMPRESULTS" { foo; printf "%1s" "$?" >&9; } | { bar; printf "%1s" "$?" >&9; } | { baz; printf "%1s" "$?" >&9; } #wait read TMPRESULTS <&8 [ 000 = "$TMPRESULTS" ] } 9>>"$TMPRESULTS" 8<"$TMPRESULTS" 

移植性に関する注意事項:

  • kshおよび最後のパイプコマンドのみを待機する同様のシェルでは、waitコメントなし

  • 最後の例ではecho -n "$?"の代わりにprintf "%1s" "$?"を使用すると、移植性が向上します。すべてのプラットフォームが-nを正しく解釈するわけではありません。

  • printf "$?"も同様に解釈します。ただし、printf "%1s"は、非常に壊れたプラットフォームでスクリプトを実行した場合に、いくつかのコーナーケースをキャッチします。 (読んでください:paranoia_mode=extremeでプログラムする場合。)

  • FD8とFD9は、複数をサポートするプラットフォームで高くなる可能性があります数字。 AFAIR POSIX準拠のシェルは、1桁のみをサポートする必要があります。

  • Debian8.2でテスト済みshbashkshashsash、さらにはcsh

回答

これは移植可能です。 POSIX準拠のシェルで動作し、「現在のディレクトリを書き込み可能にする必要がなく、同じトリックを使用する複数のスクリプトを同時に実行できます。

(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$)) 

編集:これがGillesのコメントに続くより強力なバージョンです:

(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s)) 

Edit2:そしてこれがdubiousjimのコメントに続く少し軽いバリアントです:

(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s)) 

コメント

  • これは’いくつかの理由で機能しません。 1.一時ファイルは、’が書き込まれる前に読み取ることができます。 2.予測可能な名前で共有ディレクトリに一時ファイルを作成することは安全ではありません(簡単なDoS、シンボリックリンクレース)。 3.同じスクリプトがこのトリックを複数回使用する場合、’は常に同じファイル名を使用します。 1を解決するには、パイプラインの完了後にファイルを読み取ります。 2と3を解決するには、ランダムに生成された名前の一時ファイルまたはプライベートディレクトリを使用します。
  • +1まあ$ {PIPESTATUS [0]}の方が簡単ですが、ここでの基本的な考え方は次の場合に機能します。 Gillesが言及している問題について知っている人もいます。
  • いくつかのサブシェルを保存できます:(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))。 @Johan:Bashを使用すると’簡単になることに同意しますが、状況によっては、Bashを回避する方法を知っているだけの価値があります。

回答

少し注意すれば、これは機能するはずです:

foo-status=$(mktemp -t) (foo; echo $? >$foo-status) | bar foo_status=$(cat $foo-status) 

コメント

  • jlliagreのようにクリーンアップしてみませんか? ‘ foo-statusというファイルを残しませんか?
  • @Johan:私の提案を希望する場合は、’投票することを躊躇します;)ファイルを残さないことに加えて、複数のプロセスがこれを同時に実行できるという利点があり、現在のディレクトリを書き込み可能にする必要はありません。

回答

次の「if」ブロックは、「コマンド」が成功した場合にのみ実行されます。

if command; then # ... fi 

具体的には、次のように実行できます。

haconf_out=/path/to/some/temporary/file if haconf -makerw > "$haconf_out" 2>&1; then grep -iq "Cluster already writable" "$haconf_out" # ... fi 

実行されるのはhaconf -makerw stdoutとstderrを「$ haconf_out」に保存します。 haconfからの戻り値がtrueの場合、「if」ブロックが実行され、grepは「$ haconf_out」を読み取ります。 「クラスターはすでに書き込み可能」と照合します。

パイプが自動的にクリーンアップすることに注意してください。リダイレクトを使用すると、完了時に「$ haconf_out」を削除するように注意する必要があります。

pipefailほどエレガントではありませんが、この機能の場合は正当な代替手段です。手の届かないところにあります。

回答

Alternate example for @lesmana solution, possibly simplified. Provides logging to file if desired. ===== $ cat z.sh TEE="cat" #TEE="tee z.log" #TEE="tee -a z.log" exec 8>&- 9>&- { { { { #BEGIN - add code below this line and before #END ./zz.sh echo ${?} 1>&8 # use exactly 1x prior to #END #END } 2>&1 | ${TEE} 1>&9 } 8>&1 } | exit $(read; printf "${REPLY}") } 9>&1 exit ${?} $ cat zz.sh echo "my script code..." exit 42 $ ./z.sh; echo "status=${?}" my script code... status=42 $ 

回答

(少なくともbashを使用)とset -eを組み合わせると、サブシェルを使用して、pipefailを明示的にエミュレートし、パイプエラーで終了できます

set -e foo | bar ( exit ${PIPESTATUS[0]} ) rest of program 

したがって、fooが何らかの理由で失敗した場合、プログラムの残りの部分は実行されず、スクリプトは対応するエラーコードで終了します。 (これは、fooが独自のエラーを出力することを前提としています。これは、失敗の理由を理解するのに十分です)

回答

編集:この回答は間違っていますが、興味深いので、今後のために残しておきます参照。


!をコマンドに追加すると、戻りコードが反転します。

http://tldp.org/LDP/abs/html/exit-status.html

# =========================================================== # # Preceding a _pipe_ with ! inverts the exit status returned. ls | bogus_command # bash: bogus_command: command not found echo $? # 127 ! ls | bogus_command # bash: bogus_command: command not found echo $? # 0 # Note that the ! does not change the execution of the pipe. # Only the exit status changes. # =========================================================== # 

コメント

  • これは無関係だと思います。あなたの例では、の終了コードを知りたいのですが、bogus_command
  • その答えを取り戻すことをお勧めします。
  • まあ、私は’ばかだと思われます。私は’ OPが望んでいることを実行すると考える前に、実際にスクリプトでこれを使用しました。’重要な目的には使用しませんでした

li>

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です