プロセスの標準出力と標準エラーの両方を 1 つのファイルにリダイレクトしたいと考えています。Bashでそれを行うにはどうすればよいですか?
15 に答える
do_something 2>&1 | tee -a some_file
これにより、標準エラーが標準出力にリダイレクトされ、標準出力が標準出力に出力されsome_file
ます。
stderrをstdoutにリダイレクトし、stdoutをファイルにリダイレクトできます。
some_command >file.log 2>&1
第 20 章を参照してください。 I/O リダイレクション
&>
この形式は、Bash でのみ機能する最も一般的な形式よりも優先されます。Bourne シェルでは、バックグラウンドでコマンドを実行していると解釈できます。また、フォーマットは読みやすくなっています - 2 (標準エラー) は1 (標準出力)にリダイレクトされます。
# Close standard output file descriptor
exec 1<&-
# Close standard error file descriptor
exec 2<&-
# Open standard output as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect standard error to standard output
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
これで、単純なエコーが $LOG_FILE に書き込まれ、デーモン化に役立ちます。
元の投稿者には、
何を達成する必要があるかによって異なります。スクリプトから呼び出すコマンドのイン/アウトをリダイレクトする必要があるだけの場合、答えは既に与えられています。私は、言及されたコードスニペットの後にすべてのコマンド/ビルトイン (フォークを含む) に影響を与える現在のスクリプト内でリダイレクトすることについてです。
もう 1 つの優れたソリューションは、標準エラー出力と標準出力の両方にリダイレクトし、「ストリーム」を 2 つに分割して一度にログ ファイルに記録することです。この機能は、一度に複数のファイル記述子 (ファイル、ソケット、パイプなど) に書き込み/追加できる「tee」コマンドによって提供されます。tee FILE1 FILE2 ... >(cmd1) >(cmd2) ...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
だから、最初から。/dev/stdout (ファイル記述子 #1) と /dev/stderr (ファイル記述子 #2)に接続された端末があると仮定しましょう。実際には、パイプ、ソケット、または何でもかまいません。
- ファイル記述子(FD) #3 と #4 を作成し、それぞれ #1 と #2 と同じ「場所」を指します。ファイル記述子 #1 を変更しても、ファイル記述子 #3 には影響しません。これで、ファイル記述子 #3 と #4 は、それぞれ標準出力と標準エラーを指します。これらは、実際の端末の標準出力および標準エラーとして使用されます。
- 1> >(...) は、標準出力を括弧内のコマンドにリダイレクトします
- 括弧 (サブシェル) は 'tee' を実行し、exec の標準出力 (パイプ) から読み取り、括弧内のサブシェルへの別のパイプを介して 'logger' コマンドにリダイレクトします。同時に、同じ入力をファイル記述子 #3 (端末) にコピーします。
- 2 番目の部分は、非常によく似ていますが、標準エラー記述子とファイル記述子 #2 および #4 に対して同じトリックを行うことについてです。
上記の行に加えて、次の行を含むスクリプトを実行した結果:
echo "Will end up in standard output (terminal) and /var/log/messages"
...以下のとおりであります:
$ ./my_script
Will end up in standard output (terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in standard output (terminal) and /var/log/messages
より鮮明な画像を表示するには、次の 2 行をスクリプトに追加します。
ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1
1>file.log
file.log
標準出力を fileに送信するようにシェルに指示し、2>&1
標準エラー (ファイル記述子 2) を標準出力 (ファイル記述子 1) にリダイレクトするように指示します。
注: liw.fiが指摘したように、順序は重要ですが、2>&1 1>file.log
機能しません。
不思議なことに、これは機能します:
yourcommand &> filename
ただし、これにより構文エラーが発生します。
yourcommand &>> filename
syntax error near unexpected token `>'
以下を使用する必要があります。
yourcommand 1>> filename 2>&1
簡単な答え:Command >filename 2>&1
またはCommand &>filename
説明:
「stdout」という単語を stdout に出力し、「stderror」という単語を stderror に出力する次のコードを考えてみましょう。
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
「&」演算子は、2 がファイル記述子 (stderr を指す) であり、ファイル名ではないことを bash に伝えることに注意してください。「&」を省略した場合、このコマンドはstdout
stdout に出力し、「2」という名前のファイルを作成してstderror
そこに書き込みます。
上記のコードを試すことで、リダイレクト演算子がどのように機能するかを自分で正確に確認できます。たとえば、次の 2 行のコード1,2
にリダイレクトされる2 つの記述子のどちらのファイルを変更するかによって/dev/null
、それぞれ stdout からすべてを削除し、stderror からすべてを削除します (残っているものを出力します)。
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
これで、次のコードが出力を生成しない理由を説明できます。
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
これを真に理解するには、ファイル記述子テーブルに関するこの Web ページを読むことを強くお勧めします。あなたがそれを読んだと仮定すると、先に進むことができます。Bash は左から右に処理することに注意してください。したがって、Bash は>/dev/null
最初に認識し (これは と同じです1>/dev/null
)、ファイル記述子 1 を stdout ではなく /dev/null を指すように設定します。これを行った後、Bash は右に移動して を確認し2>&1
ます。これにより、ファイル記述子 2がファイル記述子 1と同じファイルを指すように設定されます (ファイル記述子 1 自体ではありません!!!! (ポインタに関するこのリソースを参照)。詳細については)))。ファイル記述子 1 は /dev/null を指し、ファイル記述子 2 はファイル記述子 1 と同じファイルを指すため、ファイル記述子 2 も /dev/null を指します。したがって、両方のファイル記述子が /dev/null を指しており、これが出力がレンダリングされない理由です。
概念を本当に理解しているかどうかをテストするために、リダイレクトの順序を切り替えたときの出力を推測してみてください。
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
stderror
ここでの理由は、左から右に評価すると、Bash は 2>&1 を認識し、ファイル記述子 2 がファイル記述子 1 と同じ場所、つまり stdout を指すように設定するためです。次に、ファイル記述子 1 (>/dev/null = 1>/dev/null であることを思い出してください) を >/dev/null を指すように設定し、通常は標準出力に送信されるすべてのものを削除します。したがって、残っているのは、サブシェルの stdout に送信されなかったもの (括弧内のコード)、つまり「stderror」だけです。興味深いことに、1 は標準出力への単なるポインターですが、ポインター 2 を 1 にリダイレクトしても2>&1
、ポインター 2 -> 1 -> 標準出力のチェーンは形成されません。そうであれば、1 を /dev/null にリダイレクトした結果、コード2>&1 >/dev/null
はポインタ チェーン 2 -> 1 -> /dev/null を与えるため、上で見たものとは対照的に、コードは何も生成しません。
最後に、これを行うためのより簡単な方法があることに注意してください。
セクション 3.6.4 here&>
から、演算子を使用してstdout と stderr の両方をリダイレクトできることがわかります。したがって、任意のコマンドの stderr と stdout の両方の出力を\dev\null
(出力を削除する)
にリダイレクトする$ command &> /dev/null
には、 orと入力するだけです。
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
重要ポイント:
- ファイル記述子はポインターのように動作します (ただし、ファイル記述子はファイル ポインターと同じではありません)。
- ファイル記述子 "a" を、ファイル "f" を指すファイル記述子 "b" にリダイレクトすると、ファイル記述子 "a" は、ファイル記述子 b - ファイル "f" と同じ場所を指すようになります。ポインター a -> b -> f のチェーンを形成しません。
- 以上のことから、順番
2>&1 >/dev/null
は !=>/dev/null 2>&1
です。1 つは出力を生成し、もう 1 つは生成しません。
最後に、次の優れたリソースをご覧ください。
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
関連しています: 標準出力と標準エラーをsyslogに書き込んでいます。
ほとんど動作しますが、xinetdからではありません;(