web-dev-qa-db-ja.com

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

すべての出力を1つのファイル、デバッグログ、およびターミナルにリダイレクトする必要がある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

しかし、それは機能しません。誰かが解決策を持っていますか/原因を説明できますか?

45
Avi

一度に多くのコマンドをリダイレクトする解決策として:

#!/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を調べます。ls -lを使用して、シンボリックリンクのコンテンツもリストします。ここでの0ファイルは標準入力、1は標準出力、2は標準エラーです。コマンドがより多くのファイルを開く場合(そしてほとんどのプログラムが開く場合)、それらも表示されます。プログラムは、その標準入出力をリダイレクトまたは閉じることを選択して、01および2を再利用することもできます。

42
BatchyX

スクリプトの先頭で次のような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
43
slm

Stderrおよびstdoutをファイルに書き込み、stderrを画面に表示します(stdout上)

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

Cronに役立つため、メールでエラー(エラーのみ)を受け取ることができます

0
Lluís