tee
のとき 終了すると、さらに出力を書き込もうとするまで、コマンドを実行し続けます。次に、リーダーのないパイプに書き込もうとすると、SIGPIPE (ほとんどのシステムでは 13) を取得します。
スクリプトを変更して SIGPIPE をトラップし、適切なアクション (出力の書き込みを停止するなど) を実行すると、tee が終了した後もスクリプトを続行できるはずです。
tee
を殺すのではなく、いっそのこと 全然 logrotate
を使用 copytruncate
で 簡単にするためのオプションです。
logrotate(8)
を引用するには :
copytruncate
古いログ ファイルを移動し、必要に応じて新しいログ ファイルを作成するのではなく、コピーの作成後に元のログ ファイルをその場で切り捨てます。一部のプログラムにログファイルを閉じるように指示できない場合に使用でき、以前のログファイルに永遠に書き込み (追加) し続ける可能性があります。ファイルのコピーと切り捨ての間には非常に短いタイム スライスがあるため、一部のログ データが失われる可能性があることに注意してください。このオプションを使用すると、古いログ ファイルがそのまま残るため、作成オプションは無効になります。
「理由」の説明
要するに:書き込みが失敗 しなかった プログラムを終了させると(デフォルトで)、混乱が生じます。 find . | head -n 10
を検討してください -- find
は必要ありません head
以降、残りのハードドライブをスキャンして実行を続ける すでに必要な 10 行を取得して処理を進めています。
より良い方法:ロガー内をローテーション
tee
を使用しない次の例を検討してください。 実例として、
#!/usr/bin/env bash
file=${1:-debug.log} # filename as 1st argument
max_size=${2:-100000} # max size as 2nd argument
size=$(stat --format=%s -- "$file") || exit # Use GNU stat to retrieve size
exec >>"$file" # Open file for append
while IFS= read -r line; do # read a line from stdin
size=$(( size + ${#line} + 1 )) # add line's length + 1 to our counter
if (( size > max_size )); then # and if it exceeds our maximum...
mv -- "$file" "$file.old" # ...rename the file away...
exec >"$file" # ...and reopen a new file as stdout
size=0 # ...resetting our size counter
fi
printf '%s\n' "$line" # regardless, append to our current stdout
done
次のように実行する場合:
/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log
...これは /tmp/nginx/debug_log
に追加することから始まります 、ファイルの名前を /tmp/nginx/debug_log.old
に変更します 100KB を超えるコンテンツが存在する場合。ロガー自体がローテーションを行っているため、パイプの破損やエラーはなく、ローテーションが発生してもデータ損失ウィンドウは発生しません。すべての行が 1 つのファイルまたは別のファイルに書き込まれます。
もちろん、これをネイティブの bash に実装するのは効率的ではありませんが、上記はわかりやすい例です。 上記のロジックを実装するプログラムは数多くあります。考慮事項:
svlogd
、Runit スイートのサービス ロガーです。s6-log
、積極的に維持されている skanet スイートの代替品です。multilog
DJB Daemontools から、プロセス監視および監視ツールのこのファミリーの祖父です。