Cronjobの出力をjournaldに記録する ラッパースクリプト に取り組んでいます。
私にはいくつかの目標があります:
これまでのところ、2つの問題があるようです。
{ $_EXEC $_ARGS 2>&1 1>&7 7>&- | journaldlog error; } 7>&1 1>&2 | journaldlog info
は、ロギング関数を正確に2回実行するように見えます。1回はstderrに対して、もう1回はstdoutに対してです。これは行の順序を保持しません。私はむしろ、出力行ごとに1回関数を実行します。私はこれをbashでやめることをあきらめて、代わりにPerlを試すべきですか?私のPerlの経験が戻ってくると確信しています...最終的には。
私は多くの異なるアプローチを試しました。そのほとんどはオンラインで、特にスタック交換で見つかりました。正直言って、他に何を試したか思い出せない...現時点ではぼやけているだけだ。また、bash docsもあまり役に立ちませんでした。
リダイレクトで直接 プロセス置換 を使用できます:
gen > >(one) 2> >(two)
これにより、gen
の標準出力がone
への標準入力として、gen
の標準エラーがtwo
への入力として得られます。それらは実行可能なコマンドまたは関数である可能性があります。ここに私が使用した機能があります:
one() {
while read line
do
echo $'\e[31m'"$line"$'\e[22m'
done
}
two() {
while read line
do
echo $'\e[32m'"$line"$'\e[22m'
echo "$line" >&2
done
}
それらは入力ラインをそれぞれ赤と緑で出力し、two
も通常の標準エラーに書き込みます。ループ内でやりたいことは何でもできますし、入力を別のプログラムや必要なものに送ることもできます。
この時点で「行の順序を維持する」などのことは実際にはないことに注意してください-それらは2つの別々のストリームであり、別々のプロセスです。また、スケジューラが1つのプロセスをしばらく実行してから、他のプロセスを実行し、データが読み取られるまでカーネルパイプバッファにデータが保持される可能性があります。私は、stdoutとstderrにテストする奇数を出力する関数を使用してきましたが、それぞれから数十行を連続して取得するのが一般的です。
ターミナルに表示される順序に近いものを取得することは可能ですが、おそらくBashにはありません。 pipe
および select
を使用するCプログラムは、順序付けを再構築できます(同じ理由で、表示されたものと同じであるとは限りません) :プロセスはしばらくスケジュールされない可能性があり、バックログがあると、最初に何が発生したのかわからない1)。順序が非常に重要な場合は、別のアプローチが必要であり、おそらくベースの実行可能ファイルの連携が必要です。それが難しい要件である場合は、再検討する必要があるかもしれません。
1パイプのバッファリングを制御するためのプラットフォーム固有の方法もあるかもしれませんが、それらがあなたを十分に助けることはほとんどありません。 Linuxでは、バッファサイズをシステムページサイズと同じくらい小さくすることができますが、それより小さくすることはできません。私はあなたがすぐに書き込みを強制する(またはそれらが読み取られるまでブロックする)ことができるものを知りません。いずれにせよ、それらはstdioストリームの方法でソース側でバッファリングされる可能性があり、その場合、順序が表示されることはありません。
これは、異なるコマンドパイプラインにstdoutおよびstderrをキャプチャする1つの方法です。
#!/bin/bash
#
exec 3>&1 4>&2
loggerForStdout() {
# FD 3 is the original stdout
local x
while IFS= read -r x; do printf "STDOUT\t%s\n" "$x"; done >&3
}
loggerForStderr() {
# FD 4 is the original stderr
local x
while IFS= read -r x; do printf "STDERR\t%s\n" "$x"; done >&4
}
( (
# This is where your command would be put
# e.g. stdbuf -oL -eL rsync -a /from /to
#
echo this is stdout; echo this is stderr >&2
) | loggerForStdout ) 2>&1 | loggerForStderr
stdbuf -oL -eL
を使用しても、インターリーブされた行の順序を保証できるかどうかは、完全にはわかりません。
別のロガーがある場合は、最初の3行を省略できます。これらは、コードが完全な自己完結型の例になるようにのみ存在します。
Stderrとstdoutを個別に関数にリダイレクトするために使用しているものは次のとおりです。
tag() { awk '$0="['$1'] "$0; fflush();' ; }
{
{
echo std1; sleep 0.1;
echo error2 >&2; sleep 0.1;
echo error3 >&2; sleep 0.1;
echo std4; sleep 0.1;
} 2>&1 1>&3 | tag STDERR 1>&2 ;
} 3>&1 | tag STDOUT 3>&- ;
これにより、次の出力が得られます。
[STDOUT] std1
[STDERR] error2
[STDERR] error3
[STDOUT] std4
Bashとksh(93u +)で動作します。
その場合、行の順序を維持するために、コマンドの間に「sleep 0.1」を追加していることに注意してください。