215

次のようなスクリプトがあるとします。

役に立たない.sh

echo "This Is Error" 1>&2
echo "This Is Output" 

そして、別のシェルスクリプトがあります:

また役に立たない.sh

./useless.sh | sed 's/Output/Useless/'

「This Is Error」または useless.sh のその他の stderr を変数にキャプチャしたいと考えています。それをエラーと呼びましょう。

何かに stdout を使用していることに注意してください。stdout を引き続き使用したいので、この場合、stderr を stdout にリダイレクトしても役に立ちません。

だから、基本的に、私はしたいです

./useless.sh 2> $ERROR | ...

しかし、それは明らかに機能しません。

できることも知っている

./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`

しかし、それは醜く不必要です。

残念ながら、ここで答えが出ない場合は、それが私がしなければならないことです.

別の方法があることを願っています。

誰にも良いアイデアはありますか?

4

20 に答える 20

116

次のようにエラー ファイルをキャプチャする方が適切です。

ERROR=$(</tmp/Error)

catシェルはこれを認識し、データを取得するために' ' を実行する必要はありません。

より大きな問題は難しいです。簡単にできる方法はないと思います。エラーを標準出力にリダイレクトできるように、パイプライン全体をサブシェルに組み込み、最終的には最終的な標準出力をファイルに送信する必要があります。

ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )

セミコロンが必要であることに注意してください (従来のシェル (Bourne、Korn) では確かに、おそらく Bash でも)。' {}' は、囲まれたコマンドに対して I/O リダイレクトを行います。書かれているように、エラーもキャプチャしsedます。

警告:正式にテストされていないコード - 自己責任で使用してください。

于 2009-06-07T16:57:36.297 に答える
84

stderr を stdout にリダイレクトし、stdout を /dev/null にリダイレクトしてから、バッククォートを使用するか$()、リダイレクトされた stderr をキャプチャします。

ERROR=$(./useless.sh 2>&1 >/dev/null)
于 2009-06-07T16:45:51.913 に答える
75

また役に立たない.sh

useless.shこれにより、次のようなコマンドを使用してスクリプトの出力をパイプし、という名前の変数にsed保存できます。パイプの結果は、表示のために に送信されるか、別のコマンドにパイプされます。stderrerrorstdout

これを行うために必要なリダイレクトを管理するために、いくつかの追加のファイル記述子を設定します。

#!/bin/bash

exec 3>&1 4>&2 #set up extra file descriptors

error=$( { ./useless.sh | sed 's/Output/Useless/' 2>&4 1>&3; } 2>&1 )

echo "The message is \"${error}.\""

exec 3>&- 4>&- # release the extra file descriptors
于 2009-06-08T08:11:34.590 に答える
15

この質問には多くの重複がありますが、その多くは、stderrとstdout 、および終了コードをすべて同時にキャプチャしたくない、少し単純な使用シナリオを持っています。

if result=$(useless.sh 2>&1); then
    stdout=$result
else
    rc=$?
    stderr=$result
fi

成功した場合は適切な出力が期待され、失敗した場合は stderr に診断メッセージが期待される一般的なシナリオで機能します。

シェルの制御ステートメントは、内部で既に検査を行っていることに注意してください$?。だから、次のように見えるものは何でも

cmd
if [ $? -eq 0 ], then ...

不器用で一義的な言い方です

if cmd; then ...
于 2018-02-11T18:18:34.163 に答える
6
# command receives its input from stdin.
# command sends its output to stdout.
exec 3>&1
stderr="$(command </dev/stdin 2>&1 1>&3)"
exitcode="${?}"
echo "STDERR: $stderr"
exit ${exitcode}
于 2013-11-28T15:52:43.027 に答える
3

これが私がやった方法です:

#
# $1 - name of the (global) variable where the contents of stderr will be stored
# $2 - command to be executed
#
captureStderr()
{
    local tmpFile=$(mktemp)

    $2 2> $tmpFile

    eval "$1=$(< $tmpFile)"

    rm $tmpFile
}

使用例:

captureStderr err "./useless.sh"

echo -$err-

一時ファイルを使用しますしかし、少なくとも醜いものは関数にラップされています。

于 2012-07-25T23:35:26.217 に答える
2

これは興味深い問題であり、エレガントな解決策があることを願っていました。悲しいことに、Leffler 氏と同様の解決策に行き着きますが、読みやすさを向上させるために、Bash 関数内から useless を呼び出すことができることを付け加えておきます。

#!/ビン/バッシュ

関数は役に立たない{
    /tmp/useless.sh | sed 's/Output/Useless/'
}

エラー=$(役に立たない)
エコー $エラー

他の種類の出力リダイレクトはすべて、一時ファイルでサポートする必要があります。

于 2009-06-08T00:36:07.873 に答える
2

簡単な解決策

{ ERROR=$(./useless.sh 2>&1 1>&$out); } {out}>&1
echo "-"
echo $ERROR

生産します:

This Is Output
-
This Is Error
于 2019-08-15T07:55:46.933 に答える
1

この投稿は、私自身の目的のために同様の解決策を思いつくのに役立ちました:

MESSAGE=`{ echo $ERROR_MESSAGE | format_logs.py --level=ERROR; } 2>&1`

次に、MESSAGE が空の文字列でない限り、それを他のものに渡します。これにより、何らかの Python 例外で format_logs.py が失敗したかどうかがわかります。

于 2015-08-19T14:34:29.207 に答える
1

zsh で:

{ . ./useless.sh > /dev/tty } 2>&1 | read ERROR
$ echo $ERROR
( your message )
于 2015-11-14T17:04:04.510 に答える
0

一時ファイルの使用を回避したい場合は、プロセス置換を使用できる場合があります。私はまだそれをうまく機能させていません。これは私の最初の試みでした:

$ .useless.sh 2> >( ERROR=$(<) )
-bash: command substitution: line 42: syntax error near unexpected token `)'
-bash: command substitution: line 42: `<)'

それから私は試しました

$ ./useless.sh 2> >( ERROR=$( cat <() )  )
This Is Output
$ echo $ERROR   # $ERROR is empty

でも

$ ./useless.sh 2> >( cat <() > asdf.txt )
This Is Output
$ cat asdf.txt
This Is Error

そのため、プロセスの置換は一般的に正しいことを行っています...残念ながら、STDIN>( )を何かでラップ$()して変数にキャプチャしようとすると、の内容が失われます$(). $()これは、親プロセスが所有する /dev/fd のファイル記述子にアクセスできなくなったサブプロセスを起動するためだと思います。

プロセス置換により、もはや STDERR にないデータ ストリームを操作できるようになりました。残念ながら、思い通りに操作できないようです。

于 2012-05-31T03:35:10.987 に答える
0

findコマンドを使用します

find / -maxdepth 2 -iname 'tmp' -type d

デモの非スーパーユーザーとして。ディレクトリにアクセスするときに、「許可が拒否されました」と文句を言う必要があり/ます。

#!/bin/bash

echo "terminal:"
{ err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&- | tee /dev/stderr)"; } 3>&1 | tee /dev/fd/4 2>&1; out=$(cat /dev/fd/4)
echo "stdout:" && echo "$out"
echo "stderr:" && echo "$err"

出力を与える:

terminal:
find: ‘/root’: Permission denied
/tmp
/var/tmp
find: ‘/lost+found’: Permission denied
stdout:
/tmp
/var/tmp
stderr:
find: ‘/root’: Permission denied
find: ‘/lost+found’: Permission denied

terminal出力には/dev/stderr、スクリプトなしでその find コマンドを実行した場合と同じ方法のコンテンツもあります。コンテンツを$out持っています。/dev/stdout$err/dev/stderr

使用する:

#!/bin/bash

echo "terminal:"
{ err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&-)"; } 3>&1 | tee /dev/fd/4; out=$(cat /dev/fd/4)
echo "stdout:" && echo "$out"
echo "stderr:" && echo "$err"

/dev/stderrターミナル出力で見たくない場合。

terminal:
/tmp
/var/tmp
stdout:
/tmp
/var/tmp
stderr:
find: ‘/root’: Permission denied
find: ‘/lost+found’: Permission denied
于 2021-02-18T01:30:31.837 に答える