Wie bleiben die letzten 50 Zeilen in der Logdatei?

21

Ich versuche, die letzten 50 Zeilen in meiner Datei zu behalten, wobei ich jede Minute Temperatur speichere. Ich habe diesen Befehl benutzt:

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

Das Ergebnis ist jedoch eine leere Testdatei. Ich dachte, es werden die letzten 50 Zeilen der Testdatei aufgelistet und in die Testdatei eingefügt. Wenn ich diesen Befehl verwende:

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

es funktioniert gut. Die Datei test2 enthält 50 Zeilen.

Kann mir jemand erklären, wo das Problem liegt?

dorinand
quelle
2
So etwas wie rrdtool ist möglicherweise besser geeignet, um N Aufzeichnungen (unter anderem Statistiken) über einen längeren Zeitraum zu führen.
thrig
Siehe auch unix.stackexchange.com/a/147620/117549
Jeff Schaller
2
klassisches Trunkierungsproblem
Haylem
Wenn Sie Python zum Generieren Ihrer Protokolle verwenden, sollten Sie sich das loggingModul ansehen
Wayne Werner

Antworten:

29

Das Problem ist, dass Ihre Shell die Befehlspipeline einrichtet, bevor Sie die Befehle ausführen. Es geht nicht um "Ein- und Ausgabe", sondern darum, dass der Inhalt der Datei bereits verschwunden ist, bevor Tail überhaupt ausgeführt wird. Es geht so etwas wie:

  1. Die Shell öffnet die >Ausgabedatei zum Schreiben und schneidet sie ab
  2. Die Shell konfiguriert, dass für diese Ausgabe der Dateideskriptor 1 (für stdout) verwendet wird
  3. Die Shell wird ausgeführt tail.
  4. tailläuft, öffnet /home/pi/Documents/testund findet dort nichts

Es gibt verschiedene Lösungen, aber der Schlüssel ist, das Problem zu verstehen, was tatsächlich schief geht und warum.

Dies wird produzieren, was Sie suchen,

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

Erklärung:

  • $() wird als Befehlsersetzung bezeichnet, die ausgeführt wird tail -n 50 /home/pi/Documents/test
  • Die Anführungszeichen bewahren Zeilenumbrüche in der Ausgabe.
  • > /home/pi/Documents/testLeitet die Ausgabe echo "$(tail -n 50 /home/pi/Documents/test)"in dieselbe Datei um.
Rahul
quelle
Danke, es funktioniert gut! Ich habe noch eine Frage. Können Sie uns bitte Schritt für Schritt erklären, wie Ihr Verfahren funktioniert?
Dorinand
1
Aber warum in Ihrem Fall Bash nicht zuerst durchführen? Ich verstehe nicht, wie Bash den Befehl verarbeiten. Kann jemand erklären?
Dorinand
1
Das> steht auf dem Echo-Befehl, wird also ausgeführt, wenn der Echo-Befehl mit der Ausführung beginnt. Es kann nicht mit der Ausführung beginnen, bevor es nicht geschrieben wurde. Die Variablensubstitution schreibt den Befehl. Es führt den verschachtelten Befehl aus und erstellt den Echo-Befehl durch Ersetzen des Werts.
Oktobermark
Als ich versuchte , das gleiche für 44DE Protokolldatei statt 50 mit 5000 Zeilen zu verwenden, erhalte ich Fehlerbash: xrealloc: cannot allocate 18446744071562067968 bytes
Carmageddon
8

Eine andere Lösung für spongedie moreutilsDateiumleitung, bei der zuerst die Datei gelöscht wird, besteht darin, aus dem Paket Folgendes zu verwenden :

tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test
iobender
quelle
6

Dies liegt daran, dass bash die Umleitung mit der >ersten verarbeitet und den Inhalt der Datei löscht. Dann führt es den Befehl aus. Wenn Sie verwenden >>, werden die letzten 50 Zeilen an das Ende des aktuellen Inhalts der Datei angehängt. In diesem Fall müssten Sie die gleichen 50 Zeilen zweimal wiederholen.

Der Befehl funktioniert beim Umleiten in eine andere Datei wie erwartet. Hier ist eine Möglichkeit, die letzten 50 Zeilen einer Datei in eine Datei mit demselben Namen zu schreiben:

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

Dabei werden zuerst die letzten 50 Zeilen in eine temporäre Datei geschrieben, die dann mit verschoben wird mv, um die ursprüngliche Datei zu ersetzen.

Wie in den Kommentaren erwähnt, funktioniert dies nicht, wenn die Datei noch geöffnet ist. Durch das Verschieben der Datei wird auch ein neuer Inode erstellt, und möglicherweise werden Eigentümer und Berechtigungen geändert. Ein besserer Weg, dies mit einer temporären Datei zu tun, wäre:

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

Die temporäre Datei kann auch entfernt werden, wobei der Inhalt jedes Mal überschrieben wird.

clk
quelle
Danke dir. Könnten Sie mir bitte Schritt für Schritt erklären, welche Bash ausgeführt wird? Ich kann mir nicht vorstellen, wie es funktioniert.
Dorinand
tail -50 /home/pi/Documents/test >/tmp/foo && cat /tmp/foo >/home/pi/Documents/test
Steve
1
Beachten Sie, dass dies nicht funktioniert, wenn die Protokolldatei während des Protokollierungsprozesses noch geöffnet ist (wodurch die Protokollierung in der ursprünglichen, gelöschten Datei fortgesetzt wird). tempfile + move führt zu einer neuen Inode (unterbricht alle Hardlinks) und möglicherweise zu einem anderen Besitzer oder einer anderen Dauerwelle. tail ... > temp ; cat temp > orig ; rm -f tempfunktioniert.
cas
4

Da Sie das Hauptproblem bei der Shell-Umleitung gesehen haben, haben Sie hier eine alternative Möglichkeit, eine Datei auf die letzten 50 Zeilen zu beschränken:

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

Die harte Arbeit erledigt (GNU) sed mit der Funktion -i"In-Place-Bearbeitung", die im Hintergrund funktioniert, indem sie die Ausgabe in einer temporären Datei erstellt. Der Rest der Zeilen richtet die Mathematik für die Operation von sed ein, nämlich:

  1. Zähle die Zeilen in der Datei ( wc) und subtrahiere dann 50; weise das zu n.
  2. Wenn dies n positiv ist, führen Sie den Befehl sed aus, um die Zeilen 1 bis n zu löschen.
Jeff Schaller
quelle
4
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

printfwird verwendet, um Befehle (einen pro Zeile) weiterzuleiten ed. Die edBefehle sind:

  • 1,$-50d - Alle bis auf die letzten 50 Zeilen löschen
  • w - Geänderte Datei zurück auf die Festplatte schreiben

Da keine Umleitungen erforderlich sind, kann die Shell die Ausgabedatei nicht überschreiben, bevor sie gelesen wurde.

Im Gegensatz zu den meisten Formen der direkten Bearbeitung (bei denen in der Regel nur die direkte Bearbeitung simuliert wird, indem eine temporäre Datei erstellt und dann über dem Original umbenannt wird) wird eddie ursprüngliche Datei tatsächlich bearbeitet. und Eigentümer, Gruppe und Berechtigungen - tempfile + mv ändert immer den Inode und kann je nach Umständen die anderen ändern.

cas
quelle
4

Auf einer etwas anderen Spur können Sie logrotate(8)die Protokolldateien regelmäßig in inkrementell benannte Dateien sichern und dann alte löschen.

Auf diese Weise werden die Hauptprotokolldateien des Systems verwaltet, um zu verhindern, dass sie zu lang werden.

CSM
quelle