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

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

werden … wo die Zeit ist Präfix ist der Zeitpunkt, zu dem die Zeile gedruckt wurde. Wie kann ich dies erreichen?

Kommentare

Antwort

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

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

Es Da keine Schleife erforderlich ist, wird auf jeder Ausgabezeile 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 dieser Server wieder hochgefahren wurde, den Sie neu gestartet haben? Führen Sie einfach ping | ts aus, Problem gelöst: D.

Kommentare

  • Wie habe ich davon nichts gewusst? ?!?!?! Dies ergänzt Schwanz -f erstaunlich! tail -f /tmp/script.results.txt | ts
  • Wenn ich keinen ‚ ts-Befehl habe, was soll ich verwenden?
  • Wenn es ‚ nicht funktioniert, versuchen Sie, stderr auf stdout umzuleiten, z ssh -v 127.0.0.1 2>&1 | ts
  • Installieren Sie mit sudo apt install moreutils unter Debian und yum install moreutils unter Fedora.
  • Ich denke, es ist nützlich, auf den Parameter -s hinzuweisen. Da dies die Laufzeit des Befehls anzeigt. Ich persönlich verwende gerne sowohl ts als auch ts -s gleichzeitig. 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

Antwort

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) wurden gedruckt und nicht als Zeitstempel einer Aktion, die gerade ausgeführt wird.

Möglicherweise möchten Sie auch überprüfen, ob in Ihrem Befehl noch keine integrierte Funktion vorhanden ist. Beispiel: ping -D ist in einigen ping -Versionen vorhanden und gibt die Zeit seit der Unix-Epoche vor jeder Zeile aus. Wenn Ihr Befehl jedoch keine eigene Methode enthält Es gibt einige Methoden und Werkzeuge, die unter anderem verwendet werden können:

POSIX-Shell

Beachten Sie, dass viele Shells ihre Zeichenfolgen intern als Zeichenfolgen speichern, wenn die Eingabe die Null enthält verkohlen Acter (\0) kann 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] ")" 

Kommentare

  • Ein Problem hierbei ist, dass viele Programme aktiviert sind Noch mehr Ausgabepufferung, wenn ihre Standardausgabe eine Pipe anstelle des Terminals ist.
  • @cjm – True. Einige Ausgabepuffer können durch Verwendung von stdbuf -o 0 verringert werden. Wenn das Programm jedoch die Ausgabepufferung manuell verarbeitet, hilft es nicht ‚ (es sei denn Es gibt eine Option zum Deaktivieren / Reduzieren der Größe des Ausgabepuffers.
  • Für Python können Sie die Zeilenpufferung mit python -u
  • @Bwmat Nr. ... for x in sys.stdin iteriert über Zeilen, ohne sie alle zuerst in den Speicher zu puffern.

  • Wenn Sie dies tun, erhalten Sie eine Pufferung … für eine in 1 1 1 1 1; schlafe 1; Echo; erledigt | python -c ‚ import sys, time; sys.stdout.write (“ “ .join ((“ “ .join ((time.strftime (“ [% Y-% m-% d% H:% M:% S] „, time.gmtime ()), Zeile)) für Zeile in sys.stdin))) ‚

Antwort

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

Es ist ein Befehlszeilenprogramm, ähnlich wie moreutils „s ts, um der Standardausgabe eines anderen Befehls Zeitstempelinformationen voranzustellen. Nützlich für Prozesse mit langer Laufzeit, bei denen Sie eine historische Aufzeichnung darüber wünschen, was so lange dauert.

Alles an gnomon weiterleiten Stellen Sie jeder Zeile einen Zeitstempel voran, 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 verstrichenen Sekunden an zwischen jeder Zeile, aber das ist konfigurierbar.

Gnomon-Demo

Kommentare

  • Sieht nach einer großartigen Alternative zu ts aus, wenn Live-Prozesse verwendet werden. Während ts besser für nicht interaktive Prozesse geeignet ist.

Antwort

Ich hätte es vorgezogen, oben zu kommentieren, aber ich kann es 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" 

The Das erste „$ |“ entlastet STDOUT. Das zweite setzt stderr als aktuellen Standardausgangskanal und entpuffert es. Da select die ursprüngliche Einstellung von $ | zurückgibt, setzen wir $ | durch Umschließen des select in ein select auf den Standardwert zurück. STDOUT.

Und ja, Sie können „n paste“ so wie es ist ausschneiden. 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)" 

Kommentare

  • Funktioniert wie ein Zauber, ohne dass nicht standardmäßige Pakete installiert werden müssen.

Antwort

Ryans Beitrag liefert eine interessante Idee, schlägt jedoch in mehrfacher Hinsicht fehl. 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 kommt später mit einem Unterschied in Sekunden. 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 statt echo

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

Warum bash -c "..." bash? Weil aufgrund der Option -c zuerst argumentiert wird nt wird $0 zugewiesen und wird in der Ausgabe nicht angezeigt. Auf der Handbuchseite Ihrer Shell finden Sie die richtige Beschreibung von -c

Testen dieser Lösung mit tail -f /var/log/syslog und (as Sie könnten wahrscheinlich raten,) die Verbindung zu meinem WLAN zu trennen und wieder herzustellen, hat die richtige Zeitstempelung angezeigt, die sowohl von date als auch von syslog Nachrichten

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

Mögliche Probleme:

Für die Lösung ist xargs erforderlich. Dies ist auf POSIX-kompatiblen Systemen verfügbar, daher sollten die meisten Unix-ähnlichen Systeme abgedeckt werden. Funktioniert offensichtlich nicht, wenn Ihr System nicht POSIX-kompatibel ist oder nicht GNU findutils

Antwort

Die meisten Antworten empfehlen die Verwendung von date, 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 Geschwindigkeit Unterschiede:

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: Anstelle von $(date +"${TIMESTAMP_FORMAT}") ist es besser, oder sogar $(exec -c date +"${TIMESTAMP_FORMAT}") beschleunigt die Ausführung.

UPD2: Bash 5 bietet EPOCHREALTIME Variable für Mikrosekunden Granularität können Sie mit diesem Befehl verwenden (nur etwa 30% langsamer als Sekunden): printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

Kommentare

  • Diese Antwort ist sehr unterschätzt! Besonders schön, um die relativen Auswirkungen auf die Leistung zu zeigen.
  • Ausgezeichneter Vorschlag, aber leider liefert das Datum keine Auflösung von weniger als einer Sekunde, was in meinem Fall eine Voraussetzung ist
  • Mit bash 5.0+ können Sie diesen Befehl verwenden: printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

Antwort

Sie können dies mit und xargs:

... | 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 das Datum grundsätzlich mit dem Eingabeargument am Ende wieder.

Kommentare

  • Die Lösung ist nahe beieinander, aber ‚ stimmt den Zeitstempel nicht richtig ab, wenn es um eine Ausgabe geht, die durch lange Zeiträume getrennt ist. Außerdem verwenden Sie ‚ Backticks und haben ‚ nicht $1 angegeben. Das ‚ ist kein guter Stil. Zitieren Sie immer die Variablen. Außerdem verwenden Sie ‚ echo, das nicht portierbar ist. ‚ ist in Ordnung, funktioniert jedoch auf einigen Systemen möglicherweise nicht ordnungsgemäß.
  • Nach dem Testen scheint es, dass Sie ‚ absolut richtig sind … kennen Sie eine Möglichkeit, date jede Zeile neu bewerten lassen, oder ist es ziemlich hoffnungslos?

Antwort

Schamloser Stecker für etwas, das ich Ich habe gerade geschrieben, um genau dieses Problem zu lösen: ets , geschrieben in Go.

Demo

Auf der Projektseite finden Sie viele Anwendungsbeispiele.

Der bemerkenswerte Unterschied zu vorhandenen Antworten und ähnlichen Angeboten besteht darin, dass ets so konzipiert ist, dass Ihr Befehl für Sie in einer Pty (Pseudo-Tty) ausgeführt wird – das heißt simuliert Ihr Befehl wird nativ in einem tty ausgeführt. Im Vergleich zur Ausgabe von Rohrleitungsbefehlen in z. ts, dies macht das Zeitstempeln größtenteils transparent und löst eine Reihe von Piping-Problemen:

  • Einige Programme puffern aggressiv, wenn Sie in eine Pipe schreiben, also Sie sehe keine Ausgabe und dann eine ganze Reihe von Ausgaben (ja, du kannst sie stdbuf, du kannst sogar stdbuf und ts in einen Alias / eine Funktion einschließen, aber wäre es nicht besser, wenn die Dinge sofort funktionieren);
  • Einige Programme deaktivieren Farbe und / oder Interaktivität beim Schreiben in eine Pipe.
  • Der Beendigungsstatus ist weg, es sei denn, Sie haben Pipefail aktiviert. Usw.

Befehle können direkt ausgeführt werden, dh Sie können Ihrer vorhandenen Befehlszeile einfach ets voranstellen, oder es können Shell-Befehle sein (wie im obigen GIF gezeigt). Wenn Sie die Ausgabe einleiten möchten, kann ets dies natürlich auch.

ets unterstützt dasselbe Zeitstempelmodi als moreutils ts: absoluter Zeitmodus, Modus für verstrichene Zeit und inkrementeller Zeitmodus. Es verwendet vernünftigere Standardeinstellungen (z. B. wird die monotone Uhr immer für abgelaufene / inkrementelle Zeitstempel verwendet) und bietet zusätzliche Unterstützung für benutzerdefinierte Zeitzonen. Hier gibt es einen detaillierten Vergleich .

Wieder https://github.com/zmwangx/ets . Probieren Sie es aus, melden Sie Fehler usw.

Antwort

Geben Sie dies ein In der Nähe des Starts Ihres Skripts

# prepend a timestamp to all output exec &> >( ts "%Y-%m-%d.%H%M.%.S " ) 

Oder geben Sie für zusätzliche Gutschrift eine Protokolldatei aus über:

script_name=foobar log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 ) echo "note: redirecting output to [${log_file}]" exec &> >( ts "%Y-%m-%d %H:%M:%.S " > ${log_file} ) 

zur Ausgabe an die Konsole und die Protokolldatei:

script_name=foobar log_file=$( printf "/tmp/${script_name}-%(%Y-%m-%d)T.%(%H%M%S)T.log" -1 ) exec &> >( ts "%Y-%m-%d %H:%M:%.S " | tee ${log_file} ) 

Die Hauptvorteile dabei sind, die Protokollierung von allem anderen zu trennen, nicht Überladen Sie den Hauptteil des Skripts, indem Sie bei jedem Befehl an tee oder ähnliches weiterleiten und keine benutzerdefinierten Protokollierungsfunktionen schreiben und sich an die einfache alte echo.

Das Programm ts befindet sich im Paket moreutils, das unter allen Umständen verfügbar sein sollte vernünftiges Verwaltungsregime. 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.