57

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

var=`myscript.sh`

また

var=$(myscript.sh)

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

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

4

6 に答える 6

57

プロセス置換、、を適切に使用して、一時ファイルなしで(配管が好きな場合)、2つの別々の変数stderrをキャプチャする非常に醜い方法があります。私はあなたのコマンドを呼び出します。次の関数を使用して、このようなコマンドを模倣できます。stdoutsourcedeclarebanana

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

bananain変数の標準出力とin変数boutの標準誤差が必要であると仮定bananaしますberr。これを実現する魔法は次のとおりです(Bash≥4のみ):

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

それで、ここで何が起こっているのですか?

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

bout=$(banana)

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

それで:

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

は引き続きboutのstdoutに割り当てられますbananaが、のstderrはbananastdoutを介して端末に表示されます(リダイレクトのおかげで)2>&1

それで:

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

bout上記と同じように動作しますが、組み込みのコンテンツを(stderr経由で)端末に表示しますdeclare。これはまもなく再利用されます。

それで:

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

berrのstderrに割り当て、 withbananaのコンテンツを表示します。berrdeclare

この時点で、端末画面に次のように表示されます。

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

stdoutを介して前のものが表示されます。

最後に、プロセス置換を使用して、これらの行のコンテンツを調達します


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

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

また、次のようbananaに変数にの戻りコードがあります。bret

. <({ 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)"

そして、これはすべて安全です。なぜなら、私たちが行っている、sourceまたは行っているものはeval、そこから取得されdeclare -p、常に適切にエスケープされるからです。


もちろん、出力を配列に入れたい場合(たとえば、mapfileBash≥4を使用している場合は– <code> readループに置き換えmapfilewhileください)、適応は簡単です。

例えば:

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)
于 2014-11-09T11:00:12.653 に答える
40

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

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

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リダイレクトのみを使用して、ある変数でstdoutをキャプチャし、別の変数でstderrをキャプチャすることはできません。これを実現するには、一時ファイル(または名前付きパイプ)を使用する必要があります。

于 2012-12-10T18:12:38.460 に答える
21

できるよ:

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

$OUTこれで、スクリプトの標準出力があり、スクリプトの$ERRエラー出力があります。

于 2012-12-10T18:12:16.990 に答える
8

簡単ですが、エレガントではありません。stderrを一時ファイルにリダイレクトしてから、読み返します。

TMP=$(mktemp)
var=$(myscript.sh 2> "$TMP")
err=$(cat "$TMP")
rm "$TMP"
于 2012-12-10T18:15:33.600 に答える
5

stderrとstdoutをキャプチャしてbash内の変数を分離する方法は見つかりませんでしたが、両方を同じ変数に送信します…</ p>

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

…次に、終了ステータス「$?」を確認し、$resultのデータに対して適切に動作します。

于 2014-01-09T20:09:54.950 に答える
-1
# NAME
#   capture - capture the stdout and stderr output of a command
# SYNOPSIS
#   capture <result> <error> <command>
# DESCRIPTION
#   This shell function captures the stdout and stderr output of <command> in
#   the shell variables <result> and <error>.
# ARGUMENTS
#   <result>  - the name of the shell variable to capture stdout
#   <error>   - the name of the shell variable to capture stderr
#   <command> - the command to execute
# ENVIRONMENT
#   The following variables are mdified in the caller's context:
#    - <result>
#    - <error>
# RESULT
#   Retuns the exit code of <command>.
# SOURCE
capture ()
{
    # Name of shell variable to capture the stdout of command.
    result=$1
    shift

    # Name of shell variable to capture the stderr of command.
    error=$1
    shift

    # Local AWK program to extract the error, the result, and the exit code
    # parts of the captured output of command.
    local evaloutput='
        {
            output [NR] = $0
        }
        END \
        {
            firstresultline = NR - output [NR - 1] - 1
            if (Var == "error") \
            {
                for (i = 1; i < firstresultline; ++ i)
                {
                    printf ("%s\n", output [i])
                }
            }
            else if (Var == "result") \
            {
                for (i = firstresultline; i < NR - 1; ++ i)
                {
                    printf ("%s\n", output [i])
                }
            }
            else \
            {
                printf ("%d", output [NR])
            }
        }'

    # Capture the stderr and stdout output of command, as well as its exit code.
    local output="$(
    {
        local stdout
        stdout="$($*)"
        local exitcode=$?
        printf "\n%s\n%d\n%d\n" \
               "$stdout" "$(echo "$stdout" | wc -l)" "$exitcode"
    } 2>&1)"

    # extract the stderr, the stdout, and the exit code parts of the captured
    # output of command.
    printf -v $error "%s" \
                     "$(echo "$output" | gawk -v Var="error" "$evaloutput")"
    printf -v $result "%s" \
                      "$(echo "$output" | gawk -v Var="result" "$evaloutput")"
    return $(echo "$output" | gawk "$evaloutput")
}
于 2015-02-15T10:11:50.520 に答える