ログファイルに最後の50行を保持する方法


21

毎分温度を保存するファイルに最後の50行を保持しようとしています。私はこのコマンドを使用しました:

tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test

しかし、結果は空のテストファイルです。テストファイルの最後の50行をリストし、テストファイルに挿入すると思いました。このコマンドを使用すると:

tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test2

正常に動作しています。test2ファイルには50行があります。

誰が問題がどこにあるのか説明できますか?


2
rrdtoolのようなものは、(他の統計の中でも)Nレコードを長期にわたって保持するのにより適している場合があります。
-thrig


2
古典的な切り捨ての問題
ヘイレム

pythonを使用してログを生成する場合は、loggingモジュールを調べる必要があります
ウェインワーナー

回答:


29

問題は、コマンドを実行する前にシェルがコマンドパイプラインを設定していることです。それは「入力と出力」の問題ではなく、ファイルの内容がtailが実行される前にすでになくなっているということです。次のようになります:

  1. シェルは>出力ファイルを書き込み用に開き、切り捨てます
  2. シェルは、その出力にファイル記述子1(stdout用)を使用するように設定します
  3. シェルが実行されtailます。
  4. tail実行され、開いて/home/pi/Documents/testそこに何も見つかりません

さまざまな解決策がありますが、重要なのは、問題、実際に何が間違っているのか、そしてその理由を理解することです。

これにより、探しているものが生成されます。

echo "$(tail -n 50 /home/pi/Documents/test)" > /home/pi/Documents/test

説明 :

  • $() 実行するコマンド置換と呼ばれます tail -n 50 /home/pi/Documents/test
  • 引用符は出力の改行を保持します。
  • > /home/pi/Documents/testの出力をecho "$(tail -n 50 /home/pi/Documents/test)"同じファイルにリダイレクトします。

ありがとうございます もう一つ質問があります。手順がどのように機能するかをステップごとに説明してください。
ドリナンド

1
しかし、なぜbashが最初に実行されないのですか?bashがコマンドを処理する方法がわかりません。誰でも説明できますか?
ドリナンド

1
>はechoコマンド上にあるため、echoコマンドが実行を開始すると実行されます。書き込まれる前に実行を開始することはできません。変数置換は、コマンドを書き込むものです。ネストされたコマンドを実行し、値を代入してechoコマンドを作成します。
jobermark

50行ではなく5000行を使用して44gbログファイルに同じものを使用しようとすると、エラーが発生しますbash: xrealloc: cannot allocate 18446744071562067968 bytes
Carmageddon

8

最初のファイルを消去するファイルのリダイレクトに対する別の解決策を使用することですspongeから、moreutilsそのようなpackge:

tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test

6

これは、bashが>最初にリダイレクトを処理し、ファイルの内容を削除するためです。次に、コマンドを実行します。を使用した>>場合、最後の50行が現在ファイルにあるものの最後に追加されます。この場合、同じ50行が2回繰り返されます。

別のファイルにリダイレクトする場合、コマンドは期待どおりに機能します。ファイルの最後の50行を同じ名前のファイルに書き込む方法の1つを次に示します。

tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 && mv /home/pi/Documents/test2 /home/pi/Documents/test

これにより、最初の最後の50行が一時ファイルに書き込まれ、その後mv、元のファイルを置き換えるために使用されます。

コメントに記載されているように、ファイルがまだ開いている場合、これは機能しません。ファイルを移動すると、新しいiノードも作成され、所有権と権限が変更される場合があります。一時ファイルを使用してこれを行うより良い方法は次のとおりです。

tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 ; cat /home/pi/Documents/test2 > /home/pi/Documents/test

一時ファイルを削除することもできますが、そのたびに内容が上書きされます。


ありがとうございました。bashが実行するものをステップごとに説明していただけますか?どのように機能するか想像できません。
ドリナンド

tail -50 /home/pi/Documents/test >/tmp/foo && cat /tmp/foo >/home/pi/Documents/test
スティーブ

1
ログファイルがログプロセスによってまだ開いている場合は機能しません(元の削除されたファイルへのログが継続されます)。tempfile + moveは、新しいiノード(ハー​​ドリンクの破壊)をもたらし、場合によっては異なる所有者または権限を与えます。 tail ... > temp ; cat temp > orig ; rm -f temp動作します。
cas

4

シェルのリダイレクトに関する主な問題を確認したので、ファイルを最後の50行にプルーニングする別の方法を次に示します。

file=/path/to/the/file
n=$(( $(wc -l < "$file") - 50 ))
[[ $n -gt 0 ]] && sed -i 1,${n}d "$file"

ハードワークは(GNU)が-i「インプレース編集」機能を使用して行われます。この機能は、一時ファイルに出力を作成することにより、内部で機能します。残りの行は、sedの操作の数学を設定します。

  1. ファイル内の行をカウントし(wc)、50を引きます。それを割り当てnます。
  2. n 正の場合、sedコマンドを実行して1行目からn行目を削除します。

4
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

printfコマンド(1行に1つ)をパイプするために使用されますededコマンドは次のとおりです。

  • 1,$-50d -最後の50行を除くすべてを削除する
  • w -変更されたファイルをディスクに書き戻す

リダイレクトは含まれていないため、シェルは出力ファイルを読み取る前に上書きできません。

また、ほとんどの形式の「インプレース」編集(通常、一時ファイルを作成して元のファイル名を変更して「インプレース」編集のみをシミュレートします)とは異なり、ed実際には元のファイルを編集するため、同じiノードが保持されます(および所有者、グループ、および許可-tempfile + mvは常に iノードを変更し、状況に応じて他のノードを変更する場合があります)。


4

わずかに異なるトラックでlogrotate(8)、ログファイルを定期的に増分名前のファイルにバックアップし、古いファイルを削除するために使用できます。

これにより、メインシステムのログファイルが管理され、ファイルが長くなりすぎないようにします。

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