exec を使用して、後続のすべてのコマンドの stderr をリダイレクトします。

exec を使用して、後続のすべてのコマンドの stderr をリダイレクトします。

すべての出力をファイル、デバッグログ、およびターミナルにリダイレクトする必要があるbashファイルがあります。スクリプト内のすべてのコマンドをデバッグして記録するには、stdoutとstderrをリダイレクトする必要があります。

2>&1 | tee -a $DEBUGファイル内のすべてのコマンドに追加したくありません。私と一緒に暮らすことができます| tee -a $DEBUG

似たようなことでこれを行う方法があったと思いますexec 2>&1

現在私は次のようなものを使用しています。

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

しかし、うまくいきません。誰もが解決策を持っているか、理由を説明できますか?

答え1

次のようにスクリプトの上部でexecを使用できます。

exec > >(tee "$HOME/somefile.log") 2>&1

たとえば、

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

次のようにファイル$HOME/somefile.logと端末に出力します。

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye

答え2

一度に多数のコマンドをリダイレクトするソリューションは次のとおりです。

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

元のソリューションが機能しない理由:exec 2>&1は、標準エラー出力をコンソールでスクリプトを実行すると、コンソールになるシェルの標準出力にリダイレクトします。コマンドのパイプリダイレクトは、コマンドの標準出力のみをリダイレクトします。

観点から見るとsomecommand、標準出力は接続されたパイプに入りtee、標準エラーはシェルの標準エラーと同じファイル/擬似ファイルに入り、シェルの標準出力にリダイレクトされます。プラットフォーム上のプログラムの実行から制御できます。

これを説明する1つの実際の方法は、実際に何が起こっているのかを調べることです。

端末でシェルを実行すると、元の環境は次のようになります。

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

標準エラーを標準出力(exec 2>&1)にリダイレクトした後、デフォルトでは何も変更しません。ただし、スクリプトの標準出力をファイルにリダイレクトすると、次のような環境になります。

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

その後、シェルの標準エラーを標準出力にリダイレクトすると、次のようになります。

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

コマンドを実行すると、この環境が継承されます。コマンドを実行してそれをteeにパイプすると、コマンド環境は次のようになります。

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

したがって、コマンドの標準エラーは依然として標準エラーとしてシェルに渡されます。

/proc/[pid]/fd:useを見ると、実際にコマンド環境を見ることができ、ls -lシンボリックリンクの内容もリストできます。ここにあるファイルは0標準入力、1標準出力2、標準エラーです。コマンドがより多くのファイルを開くと(ほとんどのプログラムが実行する)、そのファイルも表示されます。プログラムは、標準入力/出力をリダイレクトまたは閉じ、それを再利用することもできます012

答え3

stderrとstdoutをファイルに書き込み、stderrを画面に表示します(stdoutから)。

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

cronに便利なので、メールでエラー(およびエラーのみ)を受け取ることができます。

答え4

マニュアルscriptページにはいくつかの警告があります。

非対話型シェルでスクリプトを実行することはお勧めできません。スクリプトの内部シェルは常に対話型であるため、予期しない結果が生じる可能性があります。

また、スクリプトは予想よりも多くの入力を読み取ることができるため、コマンドパイプラインでスクリプトを使用しないでください。

script標準入力および/または標準出力がリダイレクトされるときに使用するのは安全ではないと思います。

コマンドを実行するときは、次の操作を行います。

output=$("<command> <args>" 2> tee -a $LOGFILE >&2)

OPは、すべてのコマンドに対してまったく同じことをしたくはありませんが、関数を簡単に定義できることを理解しています。

run () { ... output=$("$(@) 2> tee -a $LOGFILE >&2"; ... }

これにより、通常の出力をキャプチャして処理し、同時に警告とエラーを記録し、同時にstderrに表示できます。

それからあなたはできます

[ $? -eq 0 ] && echo "<command> <args> succeeded! Output:\n$output\n(EOF)" >>$LOGFILE

必要に応じて、通常の出力も記録できます。

関連情報