Antworten:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Wie es funktioniert:
cat
Liest die aufgerufene Datei input.log
und druckt sie einfach in den Standardausgabestream.
Normalerweise ist die Standardausgabe mit einem Terminal verbunden, aber dieses kleine Skript enthält, |
sodass die Shell die Standardausgabe von cat
auf die Standardeingabe von umleitet sed
.
sed
Liest Daten (wie sie cat
erzeugt werden), verarbeitet sie (gemäß dem mit der -e
Option gelieferten Skript ) und druckt sie dann in ihrer Standardausgabe aus. Das Skript "s/^/$(date -R) /"
bedeutet, dass jeder Zeilenanfang durch einen vom date -R
Befehl generierten Text ersetzt wird (die allgemeine Konstruktion für den Befehl "Ersetzen" lautet :) s/pattern/replace/
.
Dann wird laut >>
bash
Redirects die Ausgabe sed
in eine Datei mit dem Namen output.log
( >
bedeutet Ersetzen des Dateiinhalts und >>
Anhängen an das Ende).
Das Problem wird $(date -R)
einmal ausgewertet, wenn Sie das Skript ausführen, sodass der aktuelle Zeitstempel am Anfang jeder Zeile eingefügt wird. Der aktuelle Zeitstempel ist möglicherweise weit von einem Moment entfernt, in dem eine Nachricht generiert wurde. Um dies zu vermeiden, müssen Sie Nachrichten so verarbeiten, wie sie in die Datei geschrieben werden, nicht mit einem Cron-Job.
Die oben beschriebene Standard-Stream-Umleitung wird als Pipe bezeichnet . Sie können es nicht nur |
zwischen Befehlen im Skript umleiten , sondern auch über eine FIFO-Datei (auch bekannt als Named Pipe ). Ein Programm schreibt in die Datei und ein anderes liest Daten und empfängt sie beim ersten Senden.
Wählen Sie ein Beispiel:
$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;
# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo
$ cat foo.log
Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz
Wie es funktioniert:
mkfifo
erstellt eine Named Pipe
while true; do sed ... ; done
führt eine Endlosschleife aus und wird bei jeder Iteration sed
mit Umleitung foo.log.fifo
zu ihrer Standardeingabe ausgeführt; sed
Blockiert das Warten auf Eingabedaten und verarbeitet dann eine empfangene Nachricht und druckt sie in die Standardausgabe, die an umgeleitet wird foo.log
.
Zu diesem Zeitpunkt müssen Sie ein neues Terminalfenster öffnen, da die Schleife das aktuelle Terminal belegt.
echo ... > foo.log.fifo
druckt eine Nachricht in ihre Standardausgabe, die in die FIFO-Datei umgeleitet wird, sed
empfängt sie und verarbeitet und schreibt in eine reguläre Datei.
Der wichtige Hinweis ist das Fifo, genau wie jedes andere Rohr keinen Sinn hat, wenn eine seiner Seiten mit keinem Prozess verbunden ist. Wenn Sie versuchen, in eine Pipe zu schreiben, wird der aktuelle Prozess blockiert, bis jemand Daten auf der anderen Seite der Pipe liest. Wenn Sie aus einer Pipe lesen möchten, wird der Prozess blockiert, bis jemand Daten in die Pipe schreibt. Die sed
Schleife im obigen Beispiel macht nichts (schläft), bis Sie dies tun echo
.
Für Ihre spezielle Situation konfigurieren Sie Ihre Anwendung einfach so, dass Protokollnachrichten in die FIFO-Datei geschrieben werden. Wenn Sie es nicht konfigurieren können, löschen Sie einfach die ursprüngliche Protokolldatei und erstellen Sie eine FIFO-Datei. Beachten Sie jedoch erneut, dass sed
Ihr Programm beim Versuch, write
auf die Datei read
zuzugreifen , blockiert wird , wenn die Schleife aus irgendeinem Grund stirbt, bis jemand vom FIFO kommt.
Der Vorteil ist der aktuelle Zeitstempel, der ausgewertet und an eine Nachricht angehängt wird, während das Programm sie in die Datei schreibt.
tailf
Um das Schreiben in das Protokoll und die Verarbeitung unabhängiger zu gestalten, können Sie zwei reguläre Dateien mit verwenden tailf
. Eine Anwendung schreibt eine Nachricht in eine Rohdatei und andere Prozesse lesen neue Zeilen (folgen Sie, um asynchron zu schreiben) und verarbeiten Daten mit Schreiben in die zweite Datei.
Nehmen wir ein Beispiel:
# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;
$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log
$ cat bar.log
Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz
Wie es funktioniert:
Führen Sie den tailf
Prozess aus, der den Schreibvorgängen folgt, bar.raw.log
und drucken Sie sie in die Standardausgabe, die in die Endlosschleife umgeleitet while read ... echo
wird. Diese Schleife führt zwei Aktionen aus: Lesen von Daten von der Standardeingabe in eine aufgerufene line
Puffervariable und Schreiben des generierten Zeitstempels mit den folgenden gepufferten Daten in die bar.log
.
Schreiben Sie einige Nachrichten an die bar.raw.log
. Sie müssen dies in einem separaten Terminalfenster tun, da das erste Fenster belegt ist, tailf
das den Schreibvorgängen folgt und seine Arbeit erledigt. Ziemlich einfach.
Der Vorteil ist, dass Ihre Anwendung nicht blockieren würde, wenn Sie töten tailf
. Die Nachteile sind weniger genaue Zeitstempel und das Duplizieren von Protokolldateien.
tailf
, und fügte die richtige Verwendung hinzu. Eigentlich tailf
scheint der Weg mit eleganter zu sein, aber ich habe den FIFO-Weg mit der Hoffnung verlassen, dass er für jemanden nützlich sein wird.
Sie können das ts
Perl-Skript verwenden von moreutils
:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Geändert von der Antwort von Dmitry Vasilyanov.
Im Bash-Skript können Sie die Ausgabe zeilenweise umleiten und mit Zeitstempel umbrechen.
Wann zu verwenden:
tailf
die Protokolldatei zu verwenden, wie Dmitry Vasilyanov sagte.Ein Beispiel mit dem Namen foo.sh
:
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"
Und das Ergebnis:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Wie es funktioniert
exec &>
Leiten Sie stdout und stderr an dieselbe Stelle um>( ... )
Pipe-Ausgaben an einen asynchronen inneren BefehlBeispielsweise:
Pipe-Zeitstempel und Protokoll in Datei
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
Oder drucken Sie den Zeitstempel und melden Sie sich bei stdout an
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
Speichern Sie sie dann in der /etc/crontab
Einstellung
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
Auf ts
diese Weise habe ich einen Eintrag mit einem Zeitstempel in einem Fehlerprotokoll für ein Skript erhalten, mit dem Cacti mit Statistiken eines Remote-Hosts gefüllt wird.
Um Kakteen zu testen rand
, füge ich einige zufällige Werte hinzu, die ich für Temperaturdiagramme zur Überwachung der Systemtemperatur verwende.
Pushmonstats.sh ist ein Skript, das Systemtemperaturstatistiken meines PCs sammelt und diese an einen Raspberry Pi sendet, auf dem Cacti ausgeführt wird. Vor einiger Zeit steckte das Netzwerk fest. Ich habe nur SSH-Timeouts in meinem Fehlerprotokoll. Leider keine Zeiteinträge in diesem Protokoll. Ich wusste nicht, wie ich einem Protokolleintrag einen Zeitstempel hinzufügen sollte. Nach einigen Recherchen im Internet bin ich auf diesen Beitrag gestoßen, und das habe ich mit gemacht ts
.
Um es zu testen, habe ich eine unbekannte Option verwendet rand
. Was stderr einen Fehler gab. Um es zu erfassen, leite ich es in eine temporäre Datei um. Dann benutze ich cat, um den Inhalt der Datei anzuzeigen und weiterzuleiten ts
, füge ein Zeitformat hinzu, das ich in diesem Beitrag gefunden habe, und protokolliere es schließlich in der Fehlerdatei. Dann lösche ich den Inhalt der temporären Datei, andernfalls erhalte ich doppelte Einträge für denselben Fehler.
Crontab:
* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err
Dies ergibt Folgendes in meinem Fehlerprotokoll:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Vielleicht ist dies keine sehr elegante Art, aber es funktioniert. Ich frage mich, ob es einen eleganteren Ansatz gibt.