Voranstellen eines Zeitstempels für jede Ausgabezeile eines Befehls

182

Ich möchte jeder Ausgabezeile eines Befehls einen Zeitstempel voranstellen. Zum Beispiel:

foo
bar
baz

würde werden

[2011-12-13 12:20:38] foo
[2011-12-13 12:21:32] bar
[2011-12-13 12:22:20] baz

... wobei der vorangestellte Zeitpunkt der Zeitpunkt ist, zu dem die Zeile gedruckt wurde. Wie kann ich das erreichen?


quelle

Antworten:

274

moreutils beinhaltet tswas das ganz nett macht:

command | ts '[%Y-%m-%d %H:%M:%S]'

Es ist auch keine Schleife mehr erforderlich. Auf jeder Ausgabezeile ist ein Zeitstempel angebracht.

$ echo -e "foo\nbar\nbaz" | ts '[%Y-%m-%d %H:%M:%S]'
[2011-12-13 22:07:03] foo
[2011-12-13 22:07:03] bar
[2011-12-13 22:07:03] baz

Sie möchten wissen, wann der Server wieder hochgefahren wurde, den Sie neu gestartet haben? Einfach ausführen ping | ts, Problem gelöst: D.

Mark McKinstry
quelle
8
Woher habe ich das nicht gewusst?!?!?! Dies ergänzt Schwanz -f erstaunlich! tail -f /tmp/script.results.txt | ts
Bruno Bronosky
Was ist mit in Cygwin? Gibt es etwas ähnliches? Es sieht nicht so aus, als wären Joeys weitere Utensilien da.
CrazyPenguin
3
Was soll ich verwenden, wenn ich keinen ts-Befehl habe?
Ekassis
1
Wenn es nicht funktioniert, versuchen Sie stderr umleitet zB nach stdoutssh -v 127.0.0.1 2>&1 | ts
jchook
3
Ich halte -ses für nützlich , auf den Parameter hinzuweisen . Wie das zeigt die Laufzeit des Befehls. Ich persönlich benutze gerne beides tsund ts -sgleichzeitig. Sieht ungefähr so aus: command | ts -s '(%H:%M:%.S)]' | ts '[%Y-%m-%d %H:%M:%S'. Dies stellt die Protokollzeilen wie folgt voran:[2018-12-04 08:31:00 (00:26:28.267126)] Hai <3
BrainStone
100

Erstens, wenn Sie erwarten, dass diese Zeitstempel tatsächlich ein Ereignis darstellen, denken Sie daran, dass es wichtig ist, dies so nahe wie möglich an die Zeit zu halten, die die ursprüngliche Zeile haben würde, da viele Programme eine Zeilenpufferung durchführen (einige aggressiver als andere) statt eines Zeitstempels einer stattfindenden Aktion gedruckt worden.

Möglicherweise möchten Sie auch überprüfen, ob in Ihrem Befehl noch keine Funktion dafür integriert ist. Gibt beispielsweise ping -Din einigen pingVersionen die Zeit seit der Unix-Epoche vor jeder Zeile aus. Wenn Ihr Befehl jedoch keine eigene Methode enthält, gibt es einige Methoden und Tools, die verwendet werden können, unter anderem:

POSIX-Shell

Beachten Sie, dass viele Shells ihre Zeichenfolgen intern als Zeichenfolgen speichern. Wenn die Eingabe das Nullzeichen ( \0) enthält, wird die Zeile möglicherweise vorzeitig beendet.

command | while IFS= read -r line; do printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done

GNU awk

command | gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'

Perl

command | perl -pe 'use POSIX strftime; print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'

Python

command | python -c 'import sys,time;sys.stdout.write("".join(( " ".join((time.strftime("[%Y-%m-%d %H:%M:%S]", time.localtime()), line)) for line in sys.stdin )))'

Rubin

command | ruby -pe 'print Time.now.strftime("[%Y-%m-%d %H:%M:%S] ")'
Chris Down
quelle
3
Ein Problem hierbei ist, dass viele Programme noch mehr Ausgabepuffer aktivieren, wenn ihre Standardausgabe eine Pipe anstelle des Terminals ist.
cjm
3
@ cjm - Richtig. Einige Ausgabepuffer können durch die Verwendung von stdbuf -o 0verringert werden. Wenn das Programm die Ausgabepufferung jedoch manuell verarbeitet, hilft dies nicht (es sei denn, es gibt eine Option zum Deaktivieren / Verringern der Größe des Ausgabepuffers).
Chris Down
2
Für Python können Sie die python -u
Zeilenpufferung
@Bwmat Nr. ... for x in sys.stdinIteriert über Zeilen, ohne sie zuerst alle in den Speicher zu puffern.
Chris Down
Wenn Sie dies tun, erhalten Sie eine Pufferung ... für a in 1 1 1 1 1; schlafe 1; Echo; fertig | python -c 'importiert sys, time; sys.stdout.write ("". join (("" .join ((time.strftime ("[% Y-% m-% d% H:% M:% S] ", time.gmtime ()), line)) für line in sys.stdin))) '
ChuckCottrill
41

Probieren Sie für eine zeilenweise Delta-Messung gnomon aus .

Es ist ein Befehlszeilendienstprogramm, ähnlich wie moreutils 'ts, um der Standardausgabe eines anderen Befehls Zeitstempelinformationen voranzustellen. Nützlich für lang laufende Prozesse, bei denen Sie eine historische Aufzeichnung dessen wünschen, was so lange dauert.

Wenn Sie irgendetwas an gnomon weiterleiten, wird jeder Zeile ein Zeitstempel vorangestellt, der angibt, wie lange diese Zeile die letzte Zeile im Puffer war - das heißt, wie lange es gedauert hat, bis die nächste Zeile angezeigt wurde. Standardmäßig zeigt Gnomon die Sekunden an, die zwischen den einzelnen Zeilen vergangen sind, dies ist jedoch konfigurierbar.

gnomon demo

Janus Troelsen
quelle
Sieht nach einer großartigen Alternative zu tsLive-Prozessen aus. While tsist besser für nicht interaktive Prozesse geeignet.
BrainStone
7

Ryans Beitrag bietet zwar eine interessante Idee, scheitert jedoch in mehrfacher Hinsicht. Beim Testen mit tail -f /var/log/syslog | xargs -L 1 echo $(date +'[%Y-%m-%d %H:%M:%S]') $1 ist mir aufgefallen, dass der Zeitstempel derselbe bleibt, auch wenn er stdoutspäter mit einem Unterschied in Sekunden auftritt. Betrachten Sie diese Ausgabe:

[2016-07-14 01:44:25] Jul 14 01:44:32 eagle dhclient[16091]: DHCPREQUEST of 192.168.0.78 on wlan7 to 255.255.255.255 port 67 (xid=0x411b8c21)
[2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: Joining mDNS multicast group on interface wlan7.IPv6 with address fe80::d253:49ff:fe3d:53fd.
[2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: New relevant interface wlan7.IPv6 for mDNS.

Meine vorgeschlagene Lösung ist ähnlich, bietet jedoch eine ordnungsgemäße Zeitstempelung und verwendet eher eine etwas tragbarere printfals eineecho

| xargs -L 1 bash  -c 'printf "[%s] %s\n" "$(date +%Y-%m-%d\ %H:%M:%S )" "$*" ' bash

Warum bash -c '...' bash? Weil aufgrund der -cOption das erste Argument zugewiesen wird $0und nicht in der Ausgabe angezeigt wird. Konsultieren Sie die Handbuchseite Ihrer Shell für die korrekte Beschreibung von-c

Das Testen dieser Lösung mit tail -f /var/log/syslogund (wie Sie wahrscheinlich erraten konnten) Trennen und Wiederherstellen der Verbindung zu meinem WLAN hat gezeigt, dass sowohl Nachrichten dateals auch syslogNachrichten den richtigen Zeitstempel aufweisen

Bash könnte durch eine bourne-artige Shell ersetzt werden, entweder mit kshoder dashzumindest mit denen, die eine -cOption haben.

Mögliche Probleme:

Die Lösung erfordert xargsdas Vorhandensein von POSIX-kompatiblen Systemen, sodass die meisten Unix-ähnlichen Systeme abgedeckt werden sollten. Funktioniert offensichtlich nicht, wenn Ihr System nicht POSIX-kompatibel ist oder nicht über Folgendes verfügtGNU findutils

Sergiy Kolodyazhnyy
quelle
5

Ich hätte es vorgezogen, oben zu kommentieren, aber ich kann nicht, seriös. Wie auch immer, das obige Perl-Beispiel kann wie folgt ungepuffert werden:

command | perl -pe 'use POSIX strftime; 
                    $|=1; 
                    select((select(STDERR), $| = 1)[0]);
                    print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'

Das erste '$ |' Hebt die Puffer von STDOUT auf. Der zweite setzt stderr als aktuellen Standardausgangskanal und hebt die Pufferung auf. Da select die ursprüngliche Einstellung von $ | zurückgibt, setzen wir auch $ | zurück, indem wir select in select einschließen auf die Standardeinstellung STDOUT.

Und ja, Sie können 'n Paste so ausschneiden, wie es ist. Ich habe es aus Gründen der Lesbarkeit mehrfach ausgekleidet.

Und wenn Sie wirklich präzisieren möchten (und Time :: Hires installiert haben):

command | perl -pe 'use POSIX strftime; use Time::HiRes gettimeofday;
                    $|=1; 
                    select((select(STDERR), $| = 1)[0]);
                    ($s,$ms)=gettimeofday();
                    $ms=substr(q(000000) . $ms,-6);
                    print strftime "[%Y-%m-%d %H:%M:%S.$ms]", localtime($s)'
mpersico
quelle
1
Funktioniert wie ein Zauber, ohne dass nicht standardmäßige Pakete installiert werden müssen.
Jay Taylor
2

Die meisten Antworten schlagen vor zu verwenden date, aber es ist langsam genug. Wenn Ihre Bash-Version größer als 4.2.0 ist, ist es besser, printfstattdessen eine Bash-Version zu verwenden. Wenn Sie ältere Bash-Versionen unterstützen müssen, können Sie logFunktionen abhängig von der Bash-Version erstellen :

TIMESTAMP_FORMAT='%Y-%m-%dT%H:%M:%S'
# Bash version in numbers like 4003046, where 4 is major version, 003 is minor, 046 is subminor.
printf -v BV '%d%03d%03d' ${BASH_VERSINFO[0]} ${BASH_VERSINFO[1]} ${BASH_VERSINFO[2]}
if ((BV > 4002000)); then
log() {
    ## Fast (builtin) but sec is min sample for most implementations
    printf "%(${TIMESTAMP_FORMAT})T %5d %s\n" '-1' $$ "$*"  # %b convert escapes, %s print as is
}
else
log() {
    ## Slow (subshell, date) but support nanoseconds and legacy bash versions
    echo "$(date +"${TIMESTAMP_FORMAT}") $$ $*"
}
fi

Siehe Geschwindigkeitsunterschiede:

user@host:~$time for i in {1..10000}; do printf "%(${TIMESTAMP_FORMAT})T %s\n" '-1' "Some text" >/dev/null; done

real    0m0.410s
user    0m0.272s
sys     0m0.096s
user@host:~$time for i in {1..10000}; do echo "$(date +"${TIMESTAMP_FORMAT}") Some text" >/dev/null; done

real    0m27.377s
user    0m1.404s
sys     0m5.432s

UPD: Stattdessen ist $(date +"${TIMESTAMP_FORMAT}")es besser, die Ausführung zu verwenden $(exec date +"${TIMESTAMP_FORMAT}")oder sogar $(exec -c date +"${TIMESTAMP_FORMAT}")zu beschleunigen.

Mikhail
quelle
0

Sie können dies mit dateund tun xargs:

... | xargs -L 1 echo `date +'[%Y-%m-%d %H:%M:%S]'` $1

Erläuterung:

xargs -L 1weist xargs an, den Befehl continue für jede Eingabezeile auszuführen, und übergibt dabei die erste Zeile. echo `date +'[%Y-%m-%d %H:%M:%S]'` $1Im Grunde wird das Datum mit dem Eingabeargument am Ende wiedergegeben

Ryan
quelle
2
Die Lösung ist in der Nähe, aber der Zeitstempel wird nicht richtig angezeigt, wenn die Ausgabe nach langen Zeiträumen getrennt ist. Außerdem verwenden Sie Backticks und haben nicht zitiert $1. Das ist kein guter Stil. Zitieren Sie immer die Variablen. Darüber hinaus verwenden Sie echo, was nicht portabel ist. Es ist in Ordnung, funktioniert aber auf einigen Systemen möglicherweise nicht richtig.
Sergiy Kolodyazhnyy
Nachdem Sie dies getestet haben, scheinen Sie absolut recht zu haben ... wissen Sie, wie Sie datejede Zeile neu bewerten können, oder ist es so ziemlich hoffnungslos?
Ryan