プロセスの標準出力と標準エラー出力の両方を単一のファイルにリダイレクトしたい。 Bashではどうしたらいいですか?
do_something 2>&1 | tee -a some_file
これは、stderrをstdoutに、stdoutをsome_file
にリダイレクトし、 - それをstdoutに出力します。
stderr を stdout 、および stdout をファイルにリダイレクトできます。
some_command >file.log 2>&1
http://tldp.org/LDP/abs/html/io-redirection.html を参照してください。
この形式は、bashでしか機能しない最も一般的な&>形式よりも優先されます。 Bourne Shellでは、コマンドをバックグラウンドで実行していると解釈できます。また、フォーマットは読みやすく2(STDERR)で1(STDOUT)にリダイレクトされます。
編集:コメントで指摘されているように順序を変更
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
これで、単純なエコーは$ LOG_FILEに書き込みます。デーモン化に役立ちます。
元の投稿の作者に、
それはあなたが達成する必要があるものによって異なります。あなたがあなたのスクリプトからあなたが呼び出すコマンドに出入りすることだけをリダイレクトする必要があるならば、答えはすでに与えられています。私のの目的は、 を currentスクリプト内でリダイレクトすることです。これは、上記のコードスニペットの後のすべてのコマンド/組み込みコマンド(フォークを含む)に影響します。
もう1つの優れた解決策は、「ストリーム」を2つに分割することを含む、std-err/outとloggerまたはlogファイルの両方へのリダイレクトです。この機能は、一度に複数のファイル記述子(ファイル、ソケット、パイプなど)に書き込み/追加できる '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(FD#1)と/ dev/stderr(FD#2)に接続されているとしましょう。実際には、それはパイプ、ソケット、その他何でも構いません。
上記の行とさらにこれを含むスクリプトを実行した結果。
echo "Will end up in STDOUT(terminal) and /var/log/messages"
...以下のとおりであります:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(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
はシェルにSTDOUTをファイルfile.log
に送信するように指示し、2>&1
はSTDERR(ファイル記述子2)をSTDOUT(ファイル記述子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がファイル記述子(標準エラー出力を指す)であり、ファイル名ではないことをbashに伝えます。 '&'を省略した場合、このコマンドはstdout
をstdoutに出力し、 "2"という名前のファイルを作成してそこにstderror
を書き込みます。
上記のコードを試してみることで、リダイレクト演算子がどのように機能するのかを自分で確認できます。たとえば、次の2行のコードでは、2つの記述子1,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
これを本当に理解するために、私はあなたがファイルディスクリプタテーブルのこの ウェブページを読むことを強く勧めます 。あなたがその読書をしたと仮定すれば、我々は進むことができます。 Bashプロセスは左から右へと処理されます。したがって、Bashは最初に>/dev/null
を見(これは1>/dev/null
と同じです)、ファイル記述子1を標準出力の代わりに/ 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を指すように設定し、通常標準出力に送信されるすべてのものを削除します。したがって、残っているのは、サブシェル内の標準出力(括弧内のコード)に送信されなかったもの、つまり "stderror"だけでした。興味深い点は、1は標準出力への単なるポインタですが、2>&1
を介してポインタ2を1にリダイレクトしても、ポインタ2 - > 1 - > stdoutのチェーンが形成されないことです。もしそうであれば、1を/ dev/nullにリダイレクトした結果、コード2>&1 >/dev/null
はポインタチェーン2 - > 1 - >/dev/nullを与えることになり、その結果コードは何も生成しないでしょう。 。
最後に、これを実行するためのより簡単な方法があることに注意してください。
セクション3.6.4 ここ から、演算子&>
を使用してstdoutとstderrの両方をリダイレクトできることがわかります。したがって、任意のコマンドのstderrとstdoutの両方の出力を(出力を削除する)\dev\null
にリダイレクトするには、単に$ command &> /dev/null
を入力するか、私の例では、
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
主な要点
2>&1 >/dev/null
は!= >/dev/null 2>&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へのstdOutとstderrの書き方。
それはほとんど動作しますが、xintedからは動作しません。
私は標準出力と標準エラー出力をログファイルに書き出し、標準エラー出力をコンソールに表示させる解決策を望んでいました。だから私はtee経由で標準エラー出力を複製する必要がありました。
これは私が見つけた解決策です:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
状況によっては、「配管」が必要なときに使用できます。
|&
例えば:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
または
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log ; done |& sort -h
このbashベースのソリューションは、STDOUTとSTDERRを別々に( "sort -c"のSTDERRから、または "sort -h"へのSTDERRから)パイプ処理できます。
最も簡単な方法(bash4のみ):ls * 2>&- 1>&-
。
以下の関数は、stdout/stderrとログファイルの間で出力を切り替えるプロセスを自動化するために使用できます。
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
スクリプト内の使用例
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
exec 2>&1
のようなものを使うことを検討している状況では、可能であればこのようなbash関数を使ってコードを書き直すほうが読みやすいと思います。
function myfunc(){
[...]
}
myfunc &>mylog.log
Tcshの場合は、次のコマンドを使用する必要があります。
command >& file
command &> file
を使用すると、 "Invalid null command"エラーが発生します。
@フェルナンドファブレティ
あなたがしたことに加えて、私は機能をわずかに変えて、& - クロージングを取り除きました、そして、それは私のために働きました。
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to stdout"