it-swarm.com.de

Voranstellen eines Zeitstempels für jede Ausgabezeile eines Befehls

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?

205
anon

moreutils enthält ts, was dies ganz gut macht:

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

Außerdem ist keine Schleife mehr erforderlich. Auf jeder Ausgabezeile wird 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 ist, den Sie neu gestartet haben? Renn einfach ping | ts, Problem gelöst: D.

311
Mark McKinstry

Wenn Sie erwarten, dass diese Zeitstempel tatsächlich ein Ereignis darstellen, denken Sie zunächst daran, dass es wichtig ist, dies als zeitnah zu betrachten, da viele Programme eine Zeilenpufferung durchführen (einige aggressiver als andere) wurde eher gedruckt als ein Zeitstempel einer Aktion, die stattfindet.

Möglicherweise möchten Sie auch überprüfen, ob Ihr Befehl noch über eine integrierte Funktion verfügt. Beispielsweise existiert ping -D In einigen ping -Versionen und gibt die Zeit seit der Unix-Epoche vor jeder Zeile aus. Wenn Ihr Befehl jedoch keine eigene Methode enthält, können unter anderem einige Methoden und Tools verwendet werden:

POSIX-Shell

Beachten Sie, dass viele Shells ihre Zeichenfolgen intern als Zeichenfolgen speichern. Wenn die Eingabe das Nullzeichen (\0) Enthält, kann dies dazu führen, dass die Zeile vorzeitig endet.

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 )))'

Ruby

command | Ruby -pe 'print Time.now.strftime("[%Y-%m-%d %H:%M:%S] ")'
112
Chris Down

Versuchen Sie für eine zeilenweise Delta-Messung gnomon .

Es ist ein Befehlszeilenprogramm, ähnlich wie bei 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 etwas an gnomon weiterleiten, wird jeder Zeile ein Zeitstempel vorangestellt, der angibt, wie lange diese Zeile die letzte Zeile im Puffer war - dh wie lange es gedauert hat, bis die nächste Zeile angezeigt wurde. Standardmäßig zeigt gnomon die zwischen den einzelnen Zeilen verstrichenen Sekunden an, dies ist jedoch konfigurierbar.

(gnomon demo

47
Janus Troelsen

Ryans Beitrag liefert 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 habe ich festgestellt, dass der Zeitstempel auch dann gleich bleibt, wenn stdout später mit einem Unterschied in Sekunden auseinander kommt. 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 korrekte Zeitstempelung und verwendet etwas portableres printf anstelle von echo

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

Warum bash -c '...' bash? Da aufgrund der Option -c Das erste Argument $0 Zugewiesen wird und nicht in der Ausgabe angezeigt wird. Auf der Handbuchseite Ihrer Shell finden Sie die richtige Beschreibung von -c

Das Testen dieser Lösung mit tail -f /var/log/syslog Und (wie Sie wahrscheinlich erraten haben) das Trennen und erneute Verbinden mit meinem WLAN hat die richtige Zeitstempelung gezeigt, die sowohl von date als auch von syslog Nachrichten bereitgestellt wird

Bash könnte durch eine bourneähnliche Shell ersetzt werden, entweder mit ksh oder dash, zumindest mit der Option -c.

Mögliche Probleme:

Die Lösung erfordert xargs, das auf POSIX-kompatiblen Systemen verfügbar ist. Daher sollten die meisten Unix-ähnlichen Systeme abgedeckt werden. Funktioniert offensichtlich nicht, wenn Ihr System nicht POSIX-kompatibel ist oder nicht über GNU findutils

7

Ich hätte es vorgezogen, oben zu kommentieren, aber ich kann nicht, was den Ruf betrifft. 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 '$ |' Entpuffer STDOUT. Der zweite setzt stderr als aktuellen Standardausgangskanal und puffert ihn. Da select die ursprüngliche Einstellung von $ | zurückgibt, setzen wir auch $ | zurück, indem wir die select in eine select einschließen auf die Standardeinstellung STDOUT.

Und ja, Sie können so wie es ist ausschneiden und einfügen. Ich habe es aus Gründen der Lesbarkeit mehrfach ausgekleidet.

Und wenn Sie wirklich präzise werden 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)'
6
mpersico

Die meisten Antworten schlagen vor, date zu verwenden, aber es ist langsam genug. Wenn Ihre Bash-Version größer als 4.2.0 ist, ist es besser, stattdessen printf zu verwenden. Es handelt sich um eine integrierte Bash-Version. Wenn Sie ältere Bash-Versionen unterstützen müssen, können Sie die Funktion log erstellen. Dies hängt von der Bash-Version ab:

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:

[email protected]:~$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
[email protected]:~$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: Anstelle von $(date +"${TIMESTAMP_FORMAT}") ist es besser, $(exec date +"${TIMESTAMP_FORMAT}") oder sogar $(exec -c date +"${TIMESTAMP_FORMAT}") zu verwenden, um die Ausführung zu beschleunigen.

5
Mikhail

Sie können dies mit date und xargs tun:

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

Erläuterung:

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

1
Ryan