web-dev-qa-db-ja.com

Bashでstdoutとstderrの両方をキャプチャします

私はこの構文を知っています

var=`myscript.sh`

または

var=$(myscript.sh)

myscript.shの結果(stdout)をvarにキャプチャします。両方をキャプチャする場合は、stderrstdoutにリダイレクトできます。それぞれを別々の変数に保存する方法は?

ここでの私の使用例は、戻りコードがゼロ以外の場合、stderrをエコーし​​、それ以外の場合は抑制したい場合です。これを行う方法は他にもありますが、実際に可能であれば、このアプローチはうまくいくようです。

37
djechlin

一時ファイルなしで両方をキャプチャする方法はありません。

Stderrを変数にキャプチャし、stdoutをユーザー画面に渡すことができます( here のサンプル):

exec 3>&1                    # Save the place that stdout (1) points to.
output=$(command 2>&1 1>&3)  # Run command.  stderr is captured.
exec 3>&-                    # Close FD #3.

# Or this alternative, which captures stderr, letting stdout through:
{ output=$(command 2>&1 1>&3-) ;} 3>&1

ただし、stdoutとstderrの両方をキャプチャする方法はありません。

できないのは、FDリダイレクトのみを使用して、1つの変数にstdoutをキャプチャし、別の変数にstderrをキャプチャすることです。あなたはmust一時ファイル(または名前付きパイプ)を使用してそれを実現する必要があります。

28
zb'

プロセス置換stderr、およびを使用して、一時ファイルなしで(配管が好きな場合)stdoutおよびsourceを2つの別々の変数にキャプチャする本当にreallyい方法があります_ [変数] _適切。コマンドdeclareを呼び出します。次の関数を使用して、このようなコマンドを模倣できます。

banana() {
    echo "banana to stdout"
    echo >&2 "banana to stderr"
}

変数bananabananaの標準出力と、変数boutbananaの標準エラーが必要であると仮定します。これを実現する魔法を以下に示します(Bash≥4のみ):

. <({ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)

それで、ここで何が起こっているのでしょうか?

最も内側の用語から始めましょう。

bout=$(banana)

これは、端末に表示される標準エラーであるberrの標準出力をboutに割り当てる標準的な方法です。

次に:

{ bout=$(banana); } 2>&1

bananaboutにはまだbananaのstdoutが割り当てられますが、bananaのstderrはstdout経由で端末に表示されます(リダイレクトのおかげで2>&1

次に:

{ bout=$(banana); } 2>&1; declare -p bout >&2

上記のようになりますが、boutの内容をdeclareビルトインで端末に(stderr経由で)表示します:これはすぐに再利用されます。

次に:

berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr

berrの標準エラーをbananaに割り当て、berrの内容をdeclareで表示します。

この時点で、ターミナル画面に次のように表示されます。

declare -- bout="banana to stdout"
declare -- berr="banana to stderr"

線で

declare -- bout="banana to stdout"

stderrを介して表示されます。

最終的なリダイレクト:

{ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1

前のものが標準出力経由で表示されます。

最後に、これらの行の内容を プロセス置換 からsourceに使用します。


コマンドの戻りコードについても言及しました。 bananaを次のように変更します。

banana() {
    echo "banana to stdout"
    echo >&2 "banana to stderr"
    return 42
}

次のように、変数bananabretの戻りコードもあります。

. <({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)

evalを使用することにより、ソーシングやプロセス置換を行わなくても実行できます(Bash <4でも動作します)。

eval "$({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)"

sourceingまたはevalingのみがdeclare -pから取得され、常に適切にエスケープされるため、これはすべて安全です。


もちろん、配列に出力が必要な場合(たとえば、mapfileを使用する場合、Bash≥4を使用する場合は、mapfilewhilereadに置き換えます。ループ)、適応は簡単です。

例えば:

banana() {
    printf 'banana to stdout %d\n' {1..10}
    echo >&2 'banana to stderr'
    return 42
}

. <({ berr=$({ mapfile -t bout < <(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)

戻りコード付き:

. <({ berr=$({ mapfile -t bout< <(banana; bret=$?; declare -p bret >&3); } 3>&2 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)
41
gniourf_gniourf

できるよ:

OUT=$(myscript.sh 2> errFile)
ERR=$(<errFile)

$OUTはスクリプトの標準出力を持ち、$ERRにはスクリプトのエラー出力があります。

15
anubhava

簡単だがエレガントな方法:stderrを一時ファイルにリダイレクトし、それを読み返す:

TMP=$(mktemp)
var=$(myscript.sh 2> "$TMP")
err=$(cat "$TMP")
rm "$TMP"
7
jofel

Bashで変数を分離するためにstderrとstdoutをキャプチャする方法を見つけていませんが、両方を同じ変数に送信します…

result=$( { grep "JUNK" ./junk.txt; } 2>&1 )

…その後、終了ステータス「$?」を確認し、$ resultのデータを適切に処理します。

3
jack