
Ich habe eine Protokolldatei, die nach einigen Zeiträumen kontinuierlich aktualisiert wird (neue Zeile hinzugefügt).
Ich hole alle 10 Minuten nur Fehlermeldungen aus der Datei.
Zunächst habe ich beim ersten Mal alle Zeilen in eine neue Datei mit dem passenden Muster "FEHLER GEFUNDEN" geholt, indem ichawk.
Aber nach 10 Minuten wurde einer Protokolldatei eine weitere neue Zeile hinzugefügt, daher möchte ich die Protokolldatei dort lesen, wo ich aufgehört habe. Ich möchte nicht wieder von vorne beginnen.
Kann mir jemand den besten Code oder das beste Skript hierfür vorschlagen?
Antwort1
Wenn Sie die Datei mit einem Dateideskriptor wie diesem öffnen:
exec 3< /path/to/log/file
Anschließend können Sie es weiterverarbeiten:
awk '...' <&3
Danach zeigt fd3 dorthin, wo awk
es verlassen wurde.
10 Minuten später können Sie aus demselben Shell-Aufruf heraus das folgende ausführen:
awk '...' <&3
Geben Sie den Befehl erneut ein, um die neuen Daten zu verarbeiten.
Wenn Sie die vorherige Position speichern möchten, damit Sie das Lesen von einem anderen Shell-Aufruf aus fortsetzen können, ksh93
haben Sie mit folgende Möglichkeiten:
#! /usr/bin/env ksh93
file=/path/to/some-file
offset_file=$file.offset
exec 3< "$file"
[ -f "$offset_file" ] && exec 3<#(($(<"$offset_file")))
awk '...' <&3
echo "$(3<#((CUR)))" > "$offset_file"
Oder mit zsh:
#! /usr/bin/env zsh
zmodload zsh/system
file=/path/to/some-file
offset_file=$file.offset
exec 3< $file
[ -f "$offset_file" ] && sysseek -u 3 "$(<$offset_file)"
awk '...' <&3
echo $((systell(3))) > $offset_file
Antwort2
Ich mag Stéphanes Antwort, weil sie nicht immer wieder die ganze Datei liest, also füge ich hier dieSchlag(unter Linux) Äquivalent seiner Lösung (Bash hat keine integrierte Funktion seek
oder tell
Fähigkeit). Ich hätte einen Kommentar verwendet, aber mein Ruf ist zu gering.
LASTPOS=/tmp/saved_pos
exec 3< "$1"
test -f "$LASTPOS" && STARTPOS=$(($(<$LASTPOS)+1))
tail -c "+${STARTPOS:-1}" <&3 | grep "ERROR FOUND"
grep '^pos:' /proc/self/fdinfo/3 | cut -f2 > "$LASTPOS"
Ich habe den awk
Befehl auch durch ersetzt grep
, da dies normalerweise schneller ist. Sie können die Ausgabe an einen awk
Befehl weiterleiten, wenn Sie eine weitere Verarbeitung benötigen.
Antwort3
wc -l
Ich würde es mit und versuchen tail
.
Wenn Sie Bash verwenden, sollte dies funktionieren:
#!/bin/bash
LASTLNFILE=/tmp/lastline # replace with a suitable path
test -f $LASTLNFILE && LASTLN=$(<$LASTLNFILE)
CURLN=$(wc -l $1 | cut -d' ' -f1)
if ((CURLN-LASTLN > 0)); then
tail -n $((CURLN-LASTLN)) $1
fi
echo $CURLN > $LASTLNFILE
PS: Verwenden Sie es als Filter vor Ihrem Awk-Programm, zB (vorausgesetzt, Sie haben es „newlines.sh“ genannt):
./newlines.sh <log_file> | awk -f <your_awk_program>`
Ich lasse das obige Skript als Beispiel stehen, wie mantu es nicht. Gleich nachdem ich es geschrieben hatte, wurde mir klar, dass es anfällig für einen Race Condition ist, wenn die Protokolldatei aktualisiert wird, während das Skript ausgeführt wird.
Ein reiner AWK-Ansatz ist vorzuziehen:
#!/bin/awk
BEGIN {
lastlinefile = "/tmp/lastlinefile"
getline lastline < lastlinefile
}
NR > lastline && /ERROR FOUND/ {
# do your stuff...
print
}
END { print NR > lastlinefile }