「bash -e」を使用する場合、サブシェルの出力値と終了値を取得するにはどうすればよいですか?


70

次のコードを検討してください

outer-scope.sh

#!/bin/bash
set -e
source inner-scope.sh
echo $(inner)
echo "I thought I would've died :("

inner-scope.sh

#!/bin/bash
function inner() { echo "winner"; return 1; }

outer-scope.sh呼び出しがinner()失敗したときに終了しようとしています。以来$()、サブシェルを呼び出し、これは起こりません。

関数がゼロ以外の終了コードで終了する可能性があるという事実を保持しながら、他にどのように関数の出力を取得しますか?

回答:


102

$()終了ステータスを保持します。割り当てなど、独自のステータスを持たないステートメントで使用する必要があります。

output = $(内部)

この後$?、の終了ステータスが含まれ、innerすべての種類のチェックを使用できます:

output=$(inner) || exit $?
echo $output

または:

if ! output=$(inner); then
    exit $?
fi
echo $output

または:

if output=$(inner); then
    echo $output
else
    exit $?
fi

(注:exit引数なしのbare は、同等exit $?です。つまり、最後のコマンドの終了ステータスで終了します。明確にするために2番目のフォームを使用しました。)


また、レコードの場合:sourceこの場合、完全に無関係です。あなただけ定義することができるinner()outer-scope.sh同じ結果で、ファイル。


なんで、それは$なのに?スクリプトが自動的に終了しない$()の終了ステータスが含まれています(-eが設定されている場合)。編集:決して気にしない、あなたは私の質問に答えたと思う、ありがとう!
ジャバルサッド

よく分かりません。(上記のいずれもテストしていません。)しかし、-eにはいくつかの制限があり、すべてbashのマンページで説明されています。また、について質問echo $()している場合は、行- echoコマンド-が独自の終了コード(通常は0)を持っている場合、サブシェルの終了コードが無視される可能性があります。
grawity

1
うーん、入力するif ! $(exit 1) ; then echo $?; fiとが表示されます0ifその終了値を保持する必要がある場合の方法はわかりません。
ロン・バーク

@grawity-これはDashで機能しますか?迷惑なAutoconfの動作(つまり、SunまたはIBMのコンパイラが端末に不正なオプションを出力したときにAutoconfが成功を報告する)を回避しようとしています
jww

3
if ! output=$(inner); then exit $?; fi$?のリターンコードの!代わりにのリターンコードを提供するため、リターンコード0で終了しますinner。を使用して目的の動作を得ることができますif output=$(inner); then : ; else exit $?; fiが、それは明らかにより冗長です
-SJL

26

BashFAQ / 002を参照してください。

両方(出力と終了ステータス)が必要な場合:

output=$(command)
status=$? 

特別な場合

関数のローカル変数を使用したトリッキーなケースについては、次のコードを比較してください。

f() { local    v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }

取得します:

$ f     # fooled by 'local' with inline initialization
output:data, status:0

$ g     # a good one
output:data, status:1

どうして?

サブシェルの出力を使用してlocal変数を初期化すると、終了ステータスはサブシェルではなく、コマンドの可能性が高くなります。local 0

https://stackoverflow.com/a/4421282/537554も参照してください


3
これは実際には質問に答えませんでしたが、これは今日私にとって有用なものになったため、+ 1です。
-fourpastmidnight

1
bashコマンドの終了ステータスは、常に最後に実行されたコマンドの終了ステータスです。強く型付けされた言語で多くの時間を費やすとき、「ローカル」は型指定子ではなく、単なる別のコマンドであることを忘れがちです。ここでこの点を繰り返してくれてありがとう、今日私を助けてくれました。
マルケスラー

2
うわー、今まさにこの問題に出くわし、それをクリアしました。ありがとう!
krb686

4
#!/bin/bash
set -e
source inner-scope.sh
foo=$(inner)
echo $foo
echo "I thought I would've died :("

を追加するechoと、サブシェルはスタンドアロンにならず(個別にチェックされず)、中止されません。割り当てはこの問題を回避します。

これを実行し、出力をファイルにリダイレクトして、後で処理することもできます。

tmpfile=$( mktemp )
inner > $tmpfile
cat $tmpfile
rm $tmpfile

もちろん、2番目の亜種には$ tmpfileが存在し続けます...
ダニエルベック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.