ログファイルの最後のn行のみをどのように保持しますか?


18

私が書いたスクリプトは何かをし、最後にそれ自身のログファイルにいくつかの行を追加します。ログファイルの最後のn行(たとえば、1000行)だけを保持したいと思います。これは、次の方法でスクリプトの最後に実行できます。

tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log

しかし、よりクリーンでエレガントなソリューションはありますか?おそらく、単一のコマンドで達成できますか?


logrotateエレガントなソリューションです
IPOR Sircer

1
私はそれを考えましたが、logrotate構成はスクリプト自体よりも長くなります
...-dr01

logrotateが過剰な場合、ソリューションはそれと同じくらいエレガントです。sed / awkを使用すると、1行で実行できる場合がありますが、内部に一時ファイルがないわけではないため、おそらく効率的ではなく、おそらく読みにくくなります。
kbaはモニカと一緒に

回答:


28

このようにすることは可能ですが、他の人が言ったように、最も安全なオプションは新しいファイルを生成し、そのファイルを移動して元のファイルを上書きすることです。

以下のメソッドは、行を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

スマート。追加のツールをインストールする必要がないため、これを承認済みの回答としてマークしました。私はあなたと@ John1024の答えの両方を受け入れることができたらいいのにと思います。
dr01

あなたの電話。スポンジソリューションについては知らなかったため、私はスポンジソリューションを支持しました。空のログ行を混乱させないことが保証されています。このソリューションには、ログファイルの内容に応じて、それを行う可能性があります。
パーカマーク

21

このユーティリティspongeは、この場合のためだけに設計されています。インストール済みの場合、次の2行を記述できます。

tail -n 1000 myscript.log | sponge myscript.log

通常、ファイルへの書き込みと同時にファイルから読み取ることは信頼できません。 読み取りが終了してパイプが終了spongeするmyscript.logまで、書き込みを行わないことでこれを解決しtailます。

インストール

spongeDebianのようなシステムにインストールするには:

apt-get install moreutils

spongeRHEL / CentOSシステムにインストールするには、EPELリポジトリを追加してから次を実行します。

yum install moreutils

ドキュメンテーション

からman sponge

sponge標準入力を読み取り、指定されたファイルに書き出します。シェルリダイレクトとは異なりsponge、出力ファイルを書き込む前にすべての入力を吸収します。これにより、同じファイルを読み書きするパイプラインを構築できます。


2
+1ありがとう、知らなかったsponge。あなたができない難しい方法を学んだすべての人々にとって非常に有用ですsort importantfile.txt > importantfile.txt:)
dr01

4

間違いなく「tail + mv」の方がはるかに優れています!しかし、gnu sedの場合は、試すことができます

sed -i -e :a -e '$q;N;101,$D;ba' log

3

記録のために、ed次のようなことができます

ed -s infile <<\IN
0r !tail -n 1000 infile
+1,$d
,p
q
IN

これは、開きinfilerの出力のEADS tail -n 1000 infile(すなわち、それは、1行目の前にその出力を挿入)してから、最初はファイルの末尾に1行目だったものから削除します。ファイルをその場で編集するには、,pと置き換えwます。
ただし、edソリューションは大きなファイルには適していません。


0

スクリプトでできることは、ログローテーションのロジックを実装することです。関数を介してすべてのロギングを実行します。

log()
{
   ...
}

この関数は、まず、次のようなことを行います。

printf "%s\n" "$*" >> logfile

次に、ファイルのサイズをチェックするか、何らかの方法でファイルのローテーションが必要であると判断します。その時点で、ファイルlogfile.1(存在する場合)は削除され、ファイルlogfile.0(存在する場合)は、に名前が変更されlogfile.1logfileに名前が変更されlogfile.0ます。

回転するかどうかの決定は、スクリプト自体に保持されているカウンターに基づくことができます。1000に達すると、ゼロにリセットされます。

常に厳密に1000行にトリミングすることが要件である場合、スクリプトは起動時にログファイルの行数をカウントし、それに応じてカウンターを初期化できます(またはカウントが既に1000を超えている場合は、すぐにローテーションを実行します)。

またはwc -c logfile、特定のサイズを超えることに基づいて回転を行うなどのサイズを取得できます。このようにして、状態を判断するためにファイルをスキャンする必要はありません。


0

私は使用をした、代わりにmvcpあなたは右のソフトウェアが実行されている場所にいくつかのログファイルを持ってすることができること、それを達成するためのコマンドを。たぶん、異なるユーザーのホームディレクトリまたはアプリのディレクトリにあり、すべてのログがハードリンクとして一箇所にあります。この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"

ハードリンクであるため、同等のファイル。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.