私が書いたスクリプトは何かをし、最後にそれ自身のログファイルにいくつかの行を追加します。ログファイルの最後のn行(たとえば、1000行)だけを保持したいと思います。これは、次の方法でスクリプトの最後に実行できます。
tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log
しかし、よりクリーンでエレガントなソリューションはありますか?おそらく、単一のコマンドで達成できますか?
私が書いたスクリプトは何かをし、最後にそれ自身のログファイルにいくつかの行を追加します。ログファイルの最後のn行(たとえば、1000行)だけを保持したいと思います。これは、次の方法でスクリプトの最後に実行できます。
tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log
しかし、よりクリーンでエレガントなソリューションはありますか?おそらく、単一のコマンドで達成できますか?
回答:
このようにすることは可能ですが、他の人が言ったように、最も安全なオプションは新しいファイルを生成し、そのファイルを移動して元のファイルを上書きすることです。
以下のメソッドは、行をBASHにロードするため、からの行数に応じてtail
、ログ行のコンテンツを保存するローカルシェルのメモリ使用量に影響します。
以下は、ログファイルの最後に空行が存在する場合(BASHの評価の"$(tail -1000 test.log)"
ため)に空行を削除するため、すべてのシナリオで完全に100%正確な切り捨てを行いませんが、状況によっては十分な場合があります。
$ wc -l myscript.log
475494 myscript.log
$ echo "$(tail -1000 myscript.log)" > myscript.log
$ wc -l myscript.log
1000 myscript.log
このユーティリティsponge
は、この場合のためだけに設計されています。インストール済みの場合、次の2行を記述できます。
tail -n 1000 myscript.log | sponge myscript.log
通常、ファイルへの書き込みと同時にファイルから読み取ることは信頼できません。 読み取りが終了してパイプが終了sponge
するmyscript.log
まで、書き込みを行わないことでこれを解決しtail
ます。
sponge
Debianのようなシステムにインストールするには:
apt-get install moreutils
sponge
RHEL / CentOSシステムにインストールするには、EPELリポジトリを追加してから次を実行します。
yum install moreutils
からman sponge
:
sponge
標準入力を読み取り、指定されたファイルに書き出します。シェルリダイレクトとは異なりsponge
、出力ファイルを書き込む前にすべての入力を吸収します。これにより、同じファイルを読み書きするパイプラインを構築できます。
sponge
。あなたができない難しい方法を学んだすべての人々にとって非常に有用ですsort importantfile.txt > importantfile.txt
:)
記録のために、ed
次のようなことができます
ed -s infile <<\IN
0r !tail -n 1000 infile
+1,$d
,p
q
IN
これは、開きinfile
とr
の出力のEADS tail -n 1000 infile
(すなわち、それは、1行目の前にその出力を挿入)してから、最初はファイルの末尾に1行目だったものから削除します。ファイルをその場で編集するには、,p
と置き換えw
ます。
ただし、ed
ソリューションは大きなファイルには適していません。
スクリプトでできることは、ログローテーションのロジックを実装することです。関数を介してすべてのロギングを実行します。
log()
{
...
}
この関数は、まず、次のようなことを行います。
printf "%s\n" "$*" >> logfile
次に、ファイルのサイズをチェックするか、何らかの方法でファイルのローテーションが必要であると判断します。その時点で、ファイルlogfile.1
(存在する場合)は削除され、ファイルlogfile.0
(存在する場合)は、に名前が変更されlogfile.1
、logfile
に名前が変更されlogfile.0
ます。
回転するかどうかの決定は、スクリプト自体に保持されているカウンターに基づくことができます。1000に達すると、ゼロにリセットされます。
常に厳密に1000行にトリミングすることが要件である場合、スクリプトは起動時にログファイルの行数をカウントし、それに応じてカウンターを初期化できます(またはカウントが既に1000を超えている場合は、すぐにローテーションを実行します)。
またはwc -c logfile
、特定のサイズを超えることに基づいて回転を行うなどのサイズを取得できます。このようにして、状態を判断するためにファイルをスキャンする必要はありません。
私は使用をした、代わりにmv
、cp
あなたは右のソフトウェアが実行されている場所にいくつかのログファイルを持ってすることができること、それを達成するためのコマンドを。たぶん、異なるユーザーのホームディレクトリまたはアプリのディレクトリにあり、すべてのログがハードリンクとして一箇所にあります。このmv
コマンドを使用すると、ハードリンクが失われます。cp
代わりにコマンドを使用する場合、このハードリンクを保持します。
私のコードは次のようなものです:
TMP_FILE="$(mktemp "${TMPFILENAME}.XXX")"
for FILE in "${LOGFILE_DIR}"/* ; do
tail -n $MAXLINES "${FILE}" > "${TMP_FILE}"
if [ $(ls -g "${TMP_FILE}" | awk '{print $4}') -lt $(ls -g "${FILE}" | awk '{print $4}') ] ; then
cp "${TMP_FILE}" "${FILE}"
fi
done
したがって、ファイルが同じファイルシステム上にある場合は、ユーザーにいくつかの異なる権限を与えることができ、その場合は${LOGFILE_DIR}
私と同じように長さを変更します。
それがある場合はmv
、コマンド、ファイル間および2番目のファイルには、複数の第1に接続されていないので、ハードリンクを失う-多分他にどこいくつかを置きます。
他の場所でファイルを消去することを許可しない場合、ログは一緒に残り、独自のスクリプトで適切に制御されます。
logrotate
多分もっといい。しかし、私はこの解決策に満足しています。
「」に邪魔されることはありませんが、私の場合、スペースやその他の特殊文字を含むファイルがいくつかあります。「」や{}を使用しないと、全体がうまく機能しません。
例えば、古いファイルはzip圧縮に自動化されますディレクトリが存在するOLDFILE.zip
とzip形式の取得すべてが同様のファイルにリストされている.zip_log
よう.zip_log
にも、このディレクトリにありますが、中にLOGFILE_DIR
私がして持っているが:
ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"
ハードリンクであるため、同等のファイル。
logrotate
エレガントなソリューションです