stdoutをファイルにリダイレクトする方法を知っています。
exec > foo.log
echo test
これにより、「test」がfoo.logファイルに書き込まれます。
次に、出力をログファイルにリダイレクトし、標準出力に保持します
つまり、スクリプトの外部から簡単に実行できます。
script | tee foo.log
スクリプト自体の中で宣言したい
私は試した
exec | tee foo.log
しかし、それはうまくいきませんでした。
stdoutをファイルにリダイレクトする方法を知っています。
exec > foo.log
echo test
これにより、「test」がfoo.logファイルに書き込まれます。
次に、出力をログファイルにリダイレクトし、標準出力に保持します
つまり、スクリプトの外部から簡単に実行できます。
script | tee foo.log
スクリプト自体の中で宣言したい
私は試した
exec | tee foo.log
しかし、それはうまくいきませんでした。
回答:
#!/usr/bin/env bash
# Redirect stdout ( > ) into a named pipe ( >() ) running "tee"
exec > >(tee -i logfile.txt)
# Without this, only stdout would be captured - i.e. your
# log file would not contain any error messages.
# SEE (and upvote) the answer by Adam Spiers, which keeps STDERR
# as a separate stream - I did not want to steal from him by simply
# adding his answer to mine.
exec 2>&1
echo "foo"
echo "bar" >&2
これはbash
ではなくであることに注意してくださいsh
。でスクリプトを呼び出すとsh myscript.sh
、の行に沿ってエラーが発生しますsyntax error near unexpected token '>'
。
信号トラップを使用しているtee -i
場合は、オプションを使用して、信号が発生した場合の出力の中断を回避することができます。(コメントについてはJamesThomasMoon1979に感謝します。)
パイプに書き込むかターミナルに書き込むかに応じて出力を変更するツール(ls
たとえば、色や列化された出力を使用)は、上記の構成をパイプに出力することを検出します。
色付け/列化を強制するオプションがあります(例:)ls -C --color=always
。これにより、ログファイルにもカラーコードが書き込まれ、読みにくくなります。
tee
、出力をバッファリングしないように指定しています。それがほとんどのシステムでバッファリングする場合、それはほとんどのシステムで壊れています。それtee
は私の解決策ではなく、実装の問題です。
exec
非常に強力ですが、非常に複雑です。現在の標準出力を別のファイル記述子に「バックアップ」し、後で回復することができます。Googleの「bash execチュートリアル」には、高度な機能がたくさんあります。
exec
され、文書化、新しいプロセスを開始しないように>(tee ...)
、標準的な名前付きパイプ/プロセス置換である、と&
?...もちろんのリダイレクトでは、バックグラウンド化とは何の関係もありません:-)
-i
ことをお勧めしtee
ます。そうしないと、シグナル割り込み(トラップ)によってメインスクリプトのstdoutが中断されます。たとえば、aがtrap 'echo foo' EXIT
あり、次にを押したctrl+c
場合、「foo」は表示されません。だから私はへの答えを変更しますexec &> >(tee -ia file)
。
受け入れられた回答は、STDERRを個別のファイル記述子として保持しません。つまり
./script.sh >/dev/null
bar
端末には出力せず、ログファイルにのみ出力します。
./script.sh 2>/dev/null
双方の意志の出力foo
とbar
端子に接続されています。明らかに、それは通常のユーザーが期待する動作ではありません。これは、同じログファイルに追加する2つの別々のT字プロセスを使用して修正できます。
#!/bin/bash
# See (and upvote) the comment by JamesThomasMoon1979
# explaining the use of the -i option to tee.
exec > >(tee -ia foo.log)
exec 2> >(tee -ia foo.log >&2)
echo "foo"
echo "bar" >&2
(上記では、最初はログファイルが切り捨てられないことに注意してください。その動作が必要な場合は、追加する必要があります。
>foo.log
スクリプトの先頭に移動します。)
のPOSIX.1-2008仕様でtee(1)
は、出力がバッファリングされていない、つまりラインバッファリングされていないことが要求されているため、この場合、STDOUTとSTDERRがの同じ行になる可能性がありますfoo.log
。ログファイルが何の忠実な反映となりますので、しかし、それはまた、端末上で起こりうる可能性があり、それを正確にミラーされていない場合、端末上で見られること。STDOUT行をSTDERR行から完全に分離したい場合は、2つのログファイルを使用することを検討してください。ログファイルを後で再構成できるように、各行に日付スタンププレフィックスを付けることができます。
exec > >(tee -a $LOG)
trap "kill -9 $! 2>/dev/null" EXIT
exec 2> >(tee -a $LOG >&2)
trap "kill -9 $! 2>/dev/null" EXIT
-i
ことをお勧めしtee
ます。そうしないと、シグナル割り込み(トラップ)によってスクリプトのstdoutが中断されます。たとえばtrap 'echo foo' EXIT
、次にを押した場合ctrl+c
、「foo」は表示されません。だから私はへの答えを変更しますexec > >(tee -ia foo.log)
。
. log
か. log foo.log
:sam.nipl.net/sh/log sam.nipl.net/sh/log-a
STDOUT
が最初にバッチとして表示され、次にメッセージが表示されるSTDERR
ことです。それらは通常期待されるようにインターリーブされません。
busybox、macOS bash、および非bashシェルのソリューション
受け入れられた答えは確かにbashの最良の選択です。私はbashにアクセスできないBusybox環境で作業していますが、exec > >(tee log.txt)
構文が理解できません。またexec >$PIPE
、名前付きパイプと同じ名前の通常のファイルを作成しようとして失敗し、ハングします。
うまくいけば、これはbashを持っていない他の人に役立つでしょう。
また、名前付きパイプを使用している人にとってはrm $PIPE
、VFSからパイプをリンク解除するので安全ですが、それを使用するプロセスは、完了するまで参照カウントを維持します。
$ *の使用は必ずしも安全ではないことに注意してください。
#!/bin/sh
if [ "$SELF_LOGGING" != "1" ]
then
# The parent process will enter this branch and set up logging
# Create a named piped for logging the child's output
PIPE=tmp.fifo
mkfifo $PIPE
# Launch the child process with stdout redirected to the named pipe
SELF_LOGGING=1 sh $0 $* >$PIPE &
# Save PID of child process
PID=$!
# Launch tee in a separate process
tee logfile <$PIPE &
# Unlink $PIPE because the parent process no longer needs it
rm $PIPE
# Wait for child process, which is running the rest of this script
wait $PID
# Return the error code from the child process
exit $?
fi
# The rest of the script goes here
スクリプトファイル内で、次のようにすべてのコマンドをかっこで囲みます。
(
echo start
ls -l
echo end
) | tee foo.log
{}
受け入れられた回答を使用すると、スクリプトは例外的に早く戻り( 'exec>>(tee ...)'の直後)、残りのスクリプトをバックグラウンドで実行したままにしました。その解決策をうまく機能させることができなかったので、問題の別の解決策/回避策を見つけました:
# Logging setup
logfile=mylogfile
mkfifo ${logfile}.pipe
tee < ${logfile}.pipe $logfile &
exec &> ${logfile}.pipe
rm ${logfile}.pipe
# Rest of my script
これにより、スクリプトからの出力がプロセスからパイプを通じて「tee」のサブバックグラウンドプロセスに送られ、すべてがディスクおよびスクリプトの元のstdoutに記録されます。
「exec&>」はstdoutとstderrの両方をリダイレクトすることに注意してください。必要に応じて個別にリダイレクトすることも、単にstdoutが必要な場合は「exec>」に変更することもできます。
スクリプトの最初にパイプがファイルシステムから削除されても、プロセスが完了するまで機能し続けます。rm行の後のファイル名を使用して参照することはできません。
$logfile
部分がわかりませんtee < ${logfile}.pipe $logfile &
。具体的には、これを変更して、完全に拡張されたコマンドログ行(からset -x
)をファイルにキャプチャし、に変更してstdoutの先頭に「+」を付けずに行のみを(tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &
表示し、に関するエラーメッセージを受け取りました$logfile
。tee
ラインをもう少し詳しく説明できますか?
Bash 4には、coproc
コマンドへの名前付きパイプを確立し、それを介して通信できるようにするコマンドがあります。
execに基づいたソリューションのいずれかに満足しているとは言えません。私はteeを直接使用することを好むので、スクリプトが要求されたときにteeでスクリプト自体を呼び出します。
# my script:
check_tee_output()
{
# copy (append) stdout and stderr to log file if TEE is unset or true
if [[ -z $TEE || "$TEE" == true ]]; then
echo '-------------------------------------------' >> log.txt
echo '***' $(date) $0 $@ >> log.txt
TEE=false $0 $@ 2>&1 | tee --append log.txt
exit $?
fi
}
check_tee_output $@
rest of my script
これにより、次のことが可能になります。
your_script.sh args # tee
TEE=true your_script.sh args # tee
TEE=false your_script.sh args # don't tee
export TEE=false
your_script.sh args # tee
これをカスタマイズできます。たとえば、代わりにtee = falseにしたり、代わりにTEEにログファイルを保持させたりすることができます。
これらはどちらも完璧な解決策ではありませんが、次のことを試してみてください。
exec >foo.log
tail -f foo.log &
# rest of your script
または
PIPE=tmp.fifo
mkfifo $PIPE
exec >$PIPE
tee foo.log <$PIPE &
# rest of your script
rm $PIPE
2番目のスクリプトは、スクリプトに問題が発生した場合にパイプファイルをそのままにしておきます。これは、問題である場合とそうでない場合があります(つまりrm
、後で親シェルで問題が発生する可能性があります)。
tee
-編集しました。先ほど述べたように、どちらも完璧な解決策ではありませんが、バックグラウンドプロセスは親シェルが終了すると強制終了されるので、リソースが永久に消費されることを心配する必要はありません。