Wie behält man nur die letzten n Zeilen einer Logdatei?

18

Ein Skript, das ich geschrieben habe, führt etwas aus und hängt am Ende einige Zeilen an seine eigene Protokolldatei an. Ich möchte nur die letzten n Zeilen (z. B. 1000 Zeilen) der Protokolldatei behalten. Dies kann am Ende des Skripts folgendermaßen erfolgen:

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

Aber gibt es eine sauberere und elegantere Lösung? Vielleicht mit einem einzigen Befehl erledigt?

dr01
quelle
logrotateist die elegante Lösung
Ipor Sircer
1
Ich habe darüber nachgedacht, aber die Logrotate-Konfiguration wäre länger als das Skript selbst ...
dr01
Wenn Logrotate übertrieben ist, ist Ihre Lösung so elegant wie es nur geht. Mit sed / awk können Sie dies möglicherweise in einer Zeile tun, jedoch nicht ohne eine temporäre Datei, sodass diese wahrscheinlich nicht effizienter und möglicherweise weniger lesbar ist.
kba

Antworten:

28

Dies ist möglich, aber wie bereits erwähnt, besteht die sicherste Option darin, eine neue Datei zu erstellen und diese dann zu verschieben, um das Original zu überschreiben.

Die folgende Methode lädt die Zeilen in BASH. Abhängig von der Anzahl der Zeilen von wirkt sich dies tailauf die Speichernutzung der lokalen Shell aus, um den Inhalt der Protokollzeilen zu speichern.

Das Folgende entfernt auch leere Zeilen, falls diese am Ende der Protokolldatei vorhanden sind (aufgrund des Verhaltens der BASH-Auswertung "$(tail -1000 test.log)"), sodass nicht in allen Szenarien eine wirklich 100% genaue Kürzung erzielt wird. Je nach Ihrer Situation kann dies jedoch ausreichend sein.

$ wc -l myscript.log
475494 myscript.log

$ echo "$(tail -1000 myscript.log)" > myscript.log

$ wc -l myscript.log
1000 myscript.log
Parkamark
quelle
Clever. Ich habe dies als akzeptierte Antwort markiert, da keine zusätzlichen Tools installiert werden müssen. Ich wünschte, ich könnte sowohl Ihre als auch die Antwort von @ John1024 akzeptieren.
dr01
Ihr Anruf. Ich habe die Schwammlösung überarbeitet, da ich nichts davon wusste und es garantiert nicht mit leeren Protokollzeilen zu tun hat. Diese Lösung kann dies je nach Inhalt der Protokolldatei tun.
Parkamark
21

Das Dienstprogramm wurde spongespeziell für diesen Fall entwickelt. Wenn Sie es installiert haben, können Sie Ihre zwei Zeilen schreiben:

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

Normalerweise ist es unzuverlässig, gleichzeitig mit dem Schreiben aus einer Datei zu lesen. spongeBehebt dies, indem erst geschrieben wird, myscript.lognachdem taildas Lesen beendet und die Pipe beendet wurde.

Installieren

So installieren Sie spongeauf einem Debian-ähnlichen System:

apt-get install moreutils

Um spongeauf einem RHEL / CentOS-System zu installieren , fügen Sie das EPEL-Repository hinzu und führen Sie dann Folgendes aus:

yum install moreutils

Dokumentation

Von man sponge:

spongeLiest die Standardeingabe und schreibt sie in die angegebene Datei. spongeSaugt im Gegensatz zu einer Shell-Umleitung alle Eingaben auf, bevor die Ausgabedatei geschrieben wird. Auf diese Weise können Pipelines erstellt werden, die aus derselben Datei lesen und in dieselbe Datei schreiben.

John1024
quelle
2
+1 Danke, ich wusste es nicht sponge. Sehr nützlich für alle, die auf die harte sort importantfile.txt > importantfile.txt
Tour
4

definitiv ist "tail + mv" viel besser! Aber für Gnus können wir es versuchen

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

Fürs Protokoll edkönnte man mit sowas machen

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

Dies öffnet infileund rliest die Ausgabe von tail -n 1000 infile(dh es fügt diese Ausgabe vor der ersten Zeile ein) und löscht dann von der ersten Zeile bis zum Ende der Datei. Ersetzen Sie ,pdurch w, um die Datei direkt zu bearbeiten.
Beachten Sie jedoch, dass die edLösungen nicht für große Dateien geeignet sind.

don_crissti
quelle
0

In Ihrem Skript können Sie die Logik der Protokollrotation implementieren. Führen Sie die gesamte Protokollierung über eine Funktion aus:

log()
{
   ...
}

Diese Funktion bewirkt zum einen Folgendes:

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

dann überprüft es die Größe der Datei oder entscheidet auf irgendeine Weise, dass die Datei gedreht werden muss. Zu diesem Zeitpunkt wird die Datei logfile.1, falls vorhanden, entfernt, und die Datei logfile.0, falls vorhanden, wird in umbenannt logfile.1und logfilein umbenannt logfile.0.

Die Entscheidung, ob gedreht werden soll, kann auf einem Zähler basieren, der im Skript selbst verwaltet wird. Wenn es 1000 erreicht, wird es auf Null zurückgesetzt.

Wenn immer ein striktes Trimmen auf 1000 Zeilen erforderlich ist, kann das Skript beim Start die Anzahl der Zeilen in der Protokolldatei zählen und den Zähler entsprechend initialisieren (oder wenn die Anzahl bereits 1000 erreicht oder übersteigt, die Drehung sofort ausführen).

Oder Sie können die Größe ermitteln, z. B. mit wc -c logfileund die Rotation basierend auf dem Überschreiten einer bestimmten Größe. Auf diese Weise muss die Datei niemals gescannt werden, um den Zustand festzustellen.

Kaz
quelle
0

Ich habe anstelle des Befehls mvden cpBefehl verwendet, um zu erreichen, dass Sie in der Lage sind, einige Protokolldateien an der richtigen Stelle zu haben, an der eine Software ausgeführt wird. Vielleicht in den verschiedenen User Home Dir oder in den App Dir und haben alle Logs an einem Ort als Hardlinks. Wenn Sie den mvBefehl verwenden, verlieren Sie die feste Verbindung. Wenn Sie cpstattdessen den Befehl verwenden, behalten Sie diesen festen Link bei.

Mein Code ist so etwas wie:

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   

Wenn sich die Dateien also auf demselben Dateisystem befinden, können Sie den Benutzern auch einige unterschiedliche Rechte erteilen und ${LOGFILE_DIR}die Länge wie bei mir ändern.

Wenn es der mvBefehl ist, verlieren Sie die feste Verbindung zwischen den Dateien, und Ihre zweite Datei ist nicht mehr mit der ersten verbunden - vielleicht haben Sie sie woanders platziert.

Wenn Sie andererseits nicht zulassen, dass jemand die Datei löscht, bleiben Ihre Protokolle zusammen und können über Ihr eigenes Skript gesteuert werden.

logrotatevielleicht schöner. Aber ich bin mit dieser Lösung zufrieden.

Lassen Sie sich nicht durch das "" stören, aber in meinem Fall gibt es einige Dateien mit Leerzeichen und anderen Sonderzeichen. Wenn ich das "" nicht mache oder das {}, funktioniert das Ganze nicht gut.

Zum Beispiel gibt es ein Verzeichnis, in dem ältere Dateien automatisch komprimiert werden, OLDFILE.zipund alles, was komprimiert wird, ist auch in der Datei aufgeführt, .zip_logalso auch .zip_login diesem Verzeichnis, aber in dem, mit dem LOGFILE_DIRich es habe:

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

die gleiche Datei, wie es ein fester Link ist.

Andreas Bartels
quelle