Gibt es ein Unix-Dienstprogramm, mit dem stdin Zeitstempel vorangestellt werden können?

168

Am Ende habe ich in Python ein kurzes kleines Skript dafür geschrieben, aber ich habe mich gefragt, ob es ein Dienstprogramm gibt, in das Sie Text einfügen können, das jeder Zeile einen Text voranstellt - in meinem speziellen Fall einen Zeitstempel. Im Idealfall wäre die Verwendung so etwas wie:

cat somefile.txt | prepend-timestamp

(Bevor Sie sed antworten, habe ich Folgendes versucht:

cat somefile.txt | sed "s/^/`date`/"

Der Datumsbefehl wird jedoch nur einmal ausgewertet, wenn sed ausgeführt wird, sodass jeder Zeile derselbe Zeitstempel falsch vorangestellt wird.)

Joe Shaw
quelle
Ist das cat somefile.txtein bisschen "irreführend"? Ich würde erwarten, dass es "sofort" passiert und einen einzigen Zeitstempel hat. Wäre das nicht ein besseres Testprogramm (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2)?
Ciro Santilli 13 冠状 病 六四 事件 13

Antworten:

185

Könnte versuchen mit awk:

<command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }'

Möglicherweise müssen Sie sicherstellen, dass <command>eine zeilengepufferte Ausgabe erzeugt wird, dh der Ausgabestream wird nach jeder Zeile geleert. Der hinzugefügte Zeitstempel awkist die Zeit, zu der das Zeilenende auf der Eingangsleitung angezeigt wurde.

Wenn awk Fehler anzeigt, versuchen Sie es gawkstattdessen.

Kieron
quelle
31
Auf meinem System hat 'awk' selbst gepuffert. (Dies kann für Protokolldateien problematisch sein). Ich habe dies behoben, indem ich Folgendes verwendete: awk '{Druckzeit ("% Y-% m-% dT% H:% M:% S"), $ 0; fflush (); } '
user47741
19
strftime () scheint eine GNU awk-Erweiterung zu sein. Wenn Sie also beispielsweise unter Mac OS arbeiten, verwenden Sie gawk anstelle von awk.
Joe Shaw
Es überrascht mich immer wieder, wie mächtig Bash ist. Ich hätte nicht gedacht, dass es für diese komplizierte Aufgabe eine so einfache Lösung geben würde.
Recluze
2
Für OS X-Benutzer müssen Sie dies gawkstattdessen installieren und verwenden awk. Wenn Sie MacPorts haben:sudo port install gawk
Matt Williamson
5
@teh_senaus Soweit ich weiß, hat awk's strftime()keine Millisekundengenauigkeit. Sie können jedoch den dateBefehl verwenden. Grundsätzlich schneiden Sie Nanosekunden nur auf drei Zeichen. Es wird so aussehen : COMMAND | while read -r line; do echo "$(date '+%Y-%m-%d %T.%3N') $line"; done. Beachten Sie, dass Sie% H:% M:% S mit% T abkürzen können. Wenn Sie awkaus irgendeinem Grund immer noch verwenden möchten , können Sie Folgendes tun:COMMAND | awk '{ "date +%Y-%m-%d\\ %T.%3N" | getline timestamp; print timestamp, $0; fflush(); }'
Sechs
190

tsfrom moreutils stellt jeder Eingabezeile, die Sie eingeben , einen Zeitstempel voran . Sie können es auch mit strftime formatieren.

$ echo 'foo bar baz' | ts
Mar 21 18:07:28 foo bar baz
$ echo 'blah blah blah' | ts '%F %T'
2012-03-21 18:07:30 blah blah blah
$ 

So installieren Sie es:

sudo apt-get install moreutils
Mark McKinstry
quelle
2
stderr gleichzeitig erfassen und stempeln: bad command 2>&1 | tsergibtJan 29 19:58:31 -bash: bad: command not found
rymo
Ich möchte das Zeitformat verwenden 2014 Mar 21 18:07:28. Wie bekomme ich es?
Becko
@becko - Geben Sie laut Handbuchseite eine Formatzeichenfolge strftimeals Argument an: [.. command ..] | ts '%Y %b %d %T'zum Beispiel.
Toby Speight
2
Für OS X , brew install moreutils. Um UTC auszugeben, können Sie TZ lokal einstellen, zBls -l |TZ=UTC ts '%Y-%m-%dT%H:%M:%SZ'
karmakaze
1
@Dorian das liegt daran, dass Ruby die Ausgabe puffert; Wenn Sie den Puffer vor dem Schlafengehen spülen, funktioniert es gut genug:ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
umläute
45

kommentieren , verfügbar über diesen Link oder wie annotate-outputim Debian- devscriptsPaket.

$ echo -e "a\nb\nc" > lines
$ annotate-output cat lines
17:00:47 I: Started cat lines
17:00:47 O: a
17:00:47 O: b
17:00:47 O: c
17:00:47 I: Finished with exitcode 0
Ted Percival
quelle
1
Dies ist eine großartige Lösung, funktioniert jedoch nicht mit Piped-Ausgabe oder Subshelled-Ausgabe. Die Ausgabe für ein Programm oder Skript, das Sie als Argument angeben, wird nur neu formatiert.
Slm
1
Die Annotate-Ausgabe funktioniert gut und ist einfach zu bedienen, aber WIRKLICH langsam.
Paul Brannan
31

Destillieren Sie die gegebenen Antworten auf die einfachste mögliche:

unbuffer $COMMAND | ts

Unter Ubuntu stammen sie aus den Paketen Expect-Dev und Moreutils.

sudo apt-get install expect-dev moreutils
Willem
quelle
1
Es ist expectnicht so expect-dev, aber das letztere zieht das erstere an, damit dies funktioniert. (Ubuntu 14.10, ich frage mich, ob die Binärdatei irgendwann in ein anderes Paket verschoben wurde.)
Marius Gedminas
Für Ubuntu 14.04 LTS ist es noch inexpect-dev
Willem
2
Wenn Sie die verstrichene Zeit mit Millisekundengenauigkeit erhalten möchten, verwenden Sie unbuffer $COMMAND | ts -s %.s( -swenn für die verstrichene Zeit und %.sfür die Zeit in Sekunden mit Bruchteil)
Greg Witczak
24

Wie wäre es damit?

cat somefile.txt | perl -pne 'print scalar(localtime()), " ";'

Nach Ihrem Wunsch nach Live-Zeitstempeln zu urteilen, möchten Sie vielleicht eine Protokolldatei oder etwas anderes live aktualisieren? Vielleicht

tail -f /path/to/log | perl -pne 'print scalar(localtime()), " ";' > /path/to/log-with-timestamps
jj33
quelle
4
Dieses Perl-Beispiel war besonders nützlich für mich, als ich als Benutzer an einer Ubuntu-Bereitstellung arbeitete, die anscheinend 'mawk' anstelle von 'gawk' verwendet - und nicht über die strftime-Funktion verfügt. Vielen Dank!
Opello
4
+1 dazu ist praktisch bei einer Standard-Ubuntu-Installation. Ich habe früher tail -f /path/to/log | perl -pne 'use POSIX qw(strftime); print strftime "%Y-%m-%dT%H:%M:%SZ ", gmtime();'ein Datum im ISO8601 / RFC3339-Stil anstelle des verrückten Datums erhalten, das die einfachere Version liefert.
Natevw
1
Wenn Sie die Zeitstempel-Protokollausgabe beim Eintreffen anzeigen möchten, kann es hilfreich sein, sie BEGIN { $|++; }vor der Druckanweisung hinzuzufügen , damit Perl die Ausgabe mit jeder Zeile leert.
2
Sie können das Perl-Skript weiter auf nur noch kürzen ls | perl -pne 'print localtime." "'. Die skalare Konvertierung erfolgt aufgrund der Verkettung von Zeichenfolgen.
Rahul
Warum verwenden Sie beide -pund -nOptionen? Es scheint -nredundant zu sein, wenn Sie angegeben haben -p.
Davor Cubranic
19

Ich werde das hier rauswerfen : In daemontools gibt es zwei Dienstprogramme namens tai64n und tai64nlocal , mit denen Zeitstempel vorangestellt werden, um Nachrichten zu protokollieren.

Beispiel:

cat file | tai64n | tai64nlocal
Chazomaticus
quelle
18

Kierons Antwort ist die bisher beste. Wenn Sie Probleme haben, weil das erste Programm seinen Ausgang puffert, können Sie das Entpufferungsprogramm verwenden:

unbuffer <command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; }'

Es ist standardmäßig auf den meisten Linux-Systemen installiert. Wenn Sie es selbst erstellen müssen, ist es Teil des Expect-Pakets

http://expect.nist.gov

Mark Harrison
quelle
Beachten Sie, dass ich festgestellt habe, dass 'Unbuffer' manchmal das Rückgabeergebnis des aufgerufenen Programms verbirgt - daher ist es in Jenkins oder anderen Dingen, die vom Rückgabestatus
abhängen
10

Verwenden Sie den Befehl read (1), um jeweils eine Zeile aus der Standardeingabe zu lesen, und geben Sie dann die Zeile mit dem vorangestellten Datum in dem Format Ihrer Wahl mit date (1) aus.

$ cat timestamp
#!/bin/sh
while read line
do
  echo `date` $line
done
$ cat somefile.txt | ./timestamp
Caerwyn
quelle
2
Ein bisschen schwer, da es einen neuen Prozess pro Zeile gibt, aber es funktioniert!
Joe Shaw
3

Ich bin kein Unix-Typ, aber ich denke, Sie können verwenden

gawk '{print strftime("%d/%m/%y",systime()) $0 }' < somefile.txt
PabloG
quelle
3
#! /bin/sh
unbuffer "$@" | perl -e '
use Time::HiRes (gettimeofday);
while(<>) {
        ($s,$ms) = gettimeofday();
        print $s . "." . $ms . " " . $_;
}'

quelle
2

Hier ist meine awk-Lösung (von einem Windows / XP-System mit MKS-Tools, die im Verzeichnis C: \ bin installiert sind). Es ist so konzipiert, dass das aktuelle Datum und die aktuelle Uhrzeit in der Form mm / dd hh: mm am Anfang jeder Zeile hinzugefügt werden, nachdem dieser Zeitstempel beim Lesen jeder Zeile aus dem System abgerufen wurde. Sie können natürlich das BEGIN-Muster verwenden, um den Zeitstempel einmal abzurufen und diesen Zeitstempel jedem Datensatz hinzuzufügen (egal). Ich habe dies getan, um eine Protokolldatei, die zum Zeitpunkt der Erstellung der Protokollnachricht generiert wurde, mit dem Zeitstempel zu kennzeichnen.

/"pattern"/ "C\:\\\\bin\\\\date '+%m/%d %R'" | getline timestamp;
print timestamp, $0;

Dabei ist "Muster" eine Zeichenfolge oder ein regulärer Ausdruck (ohne Anführungszeichen), die in der Eingabezeile abgeglichen werden sollen, und optional, wenn Sie alle Eingabezeilen abgleichen möchten.

Dies sollte auch auf Linux / UNIX-Systemen funktionieren. Entfernen Sie einfach das C \: \\ bin \\ und verlassen Sie die Leitung

             "date '+%m/%d %R'" | getline timestamp;

Dies setzt natürlich voraus, dass Sie mit dem Befehl "Datum" zum Standardbefehl zum Anzeigen / Festlegen von Linux / UNIX-Datumsangaben ohne spezifische Pfadinformationen gelangen (dh Ihre Umgebungs-PATH-Variable ist korrekt konfiguriert).

ElGringoGeek
quelle
Unter OS XI wurde vor dem Datumsbefehl ein Leerzeichen benötigt. Leider scheint der Befehl nicht für jede Zeile neu bewertet zu werden. Wie andere vorgeschlagen haben, versuche ich, Zeitstempel zu tail -f hinzuzufügen.
Mark Bennett
2

Mischen Sie einige Antworten von natevw und Frank Ch. Eigler.

Es hat Millisekunden, ist leistungsfähiger als datejedes Mal ein externer Befehl aufzurufen, und Perl ist auf den meisten Servern zu finden.

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday);
  use POSIX qw(strftime);
  ($s,$ms) = gettimeofday();
  print strftime "%Y-%m-%dT%H:%M:%S+$ms ", gmtime($s);
  '

Alternative Version mit Flush und Read in einer Schleife:

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday); use POSIX qw(strftime);
  $|=1;
  while(<>) {
    ($s,$ms) = gettimeofday();
    print strftime "%Y-%m-%dT%H:%M:%S+$ms $_", gmtime($s);
  }'
Keymon
quelle
1
Zwei Fehler mit den oben genannten: 1. $ ms ist nicht ausgerichtet und mit Nullen aufgefüllt. 2. Alle% -Codes im Protokoll werden entstellt. Daher habe ich anstelle der Druckzeile Folgendes verwendet: printf "%s+%06d %s", (strftime "%Y-%m-%dT%H:%M:%S", gmtime($s)), $ms, $_;
Simon Pickup
2
$ cat somefile.txt | sed "s/^/`date`/"

Sie können dies tun (mit gnu / sed ):

$ some-command | sed "x;s/.*/date +%T/e;G;s/\n/ /g"

Beispiel:

$ { echo 'line1'; sleep 2; echo 'line2'; } | sed "x;s/.*/date +%T/e;G;s/\n/ /g"
20:24:22 line1
20:24:24 line2

Natürlich können Sie auch andere Optionen des Programms verwenden Datum . Ersetzen date +%TSie einfach durch das, was Sie brauchen.

aleksandr barakin
quelle
1

Die Antwort von caerwyn kann als Unterprogramm ausgeführt werden, wodurch die neuen Prozesse pro Zeile verhindert werden:

timestamp(){
   while read line
      do
         echo `date` $line
      done
}

echo testing 123 |timestamp
Crumplecrap
quelle
3
Wie verhindert das, dass datees in jeder Zeile erzeugt wird?
Gyom
@Gyom Es verhindert nicht, dass in jeder Zeile ein Datum erzeugt wird. Es verhindert, dass die Shell jede Linie erzeugt. Ich würde hinzufügen, dass Sie in bash so etwas tun könnten: timestamp() { while read line; do echo "$(date) $line"; done; }; export -f timestampin einem Skript, das Sie als Quelle verwenden.
smbear
Wenn Sie das Datum als Unterprozess aufrufen, müssen Sie es weiterhin für jede Zeile erzeugen. Sie starten das Skript jedoch zuerst.
Peter Hansen
3
dateVerwenden Sie Bash printfim % (Datespec) T- Format , um zu verhindern, dass für jede Zeile ein Laichbefehl ausgeführt wird . So kann es aussehen timestamp() { while IFS= read -r line; do printf "%(%F %T)T %s\n" -1 "$line"; done; }. Shell-Buildins werden nicht erzeugt. Datespec Format ist wie strftime(3)zB dateFormate. Dies erfordert Bash, nicht sicher, seit welcher Version es diesen printfBezeichner unterstützt .
Mindaugas Kubilius
1

Haftungsausschluss : Die von mir vorgeschlagene Lösung ist kein in Unix integriertes Dienstprogramm.

Ich hatte vor ein paar Tagen ein ähnliches Problem. Die Syntax und die Einschränkungen der oben genannten Lösungen haben mir nicht gefallen, daher habe ich in Go schnell ein Programm zusammengestellt, um die Arbeit für mich zu erledigen.

Sie können das Tool hier überprüfen: Preftime

Im Abschnitt " Releases " des GitHub-Projekts befinden sich vorgefertigte ausführbare Dateien für Linux, MacOS und Windows .

Das Tool verarbeitet unvollständige Ausgabezeilen und hat (aus meiner Sicht) eine kompaktere Syntax.

<command> | preftime

Es ist nicht ideal, aber ich denke, ich würde es teilen, falls es jemandem hilft.

Momchil Atanasov
quelle
0

mache es mit dateund trund xargsunter OSX:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S\" | tr \"\n\" \" \"; echo \"{}\"'"
<command> | predate

Wenn Sie Millisekunden wollen:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"

Beachten Sie jedoch, dass das Datum unter OSX nicht die Option% N bietet. Sie müssen also gdate ( brew install coreutils) installieren, um schließlich zu folgendem Ergebnis zu gelangen:

alias predate="xargs -I{} sh -c 'gdate +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"
Orion Elenzil
quelle
-1

Wenn der Wert, den Sie voranstellen, in jeder Zeile gleich ist, starten Sie Emacs mit der Datei.

Ctrl + <space>

Scrollen Sie am Anfang der Datei (um diese Stelle zu markieren) bis zum Anfang der letzten Zeile (Alt +> geht zum Ende der Datei ... was wahrscheinlich auch die Umschalttaste und dann Strg betrifft + a, um an den Anfang dieser Zeile zu gelangen) und:

Ctrl + x r t

Mit diesem Befehl können Sie das gerade angegebene Rechteck einfügen (ein Rechteck mit einer Breite von 0).

2008-8-21 6:45 PM <enter>

Oder was auch immer Sie voranstellen möchten ... dann sehen Sie diesen Text vor jeder Zeile innerhalb des Rechtecks ​​mit der Breite 0.

UPDATE: Ich habe gerade festgestellt, dass Sie nicht das gleiche Datum möchten, daher funktioniert dies nicht ... obwohl Sie dies möglicherweise in Emacs mit einem etwas komplizierteren benutzerdefinierten Makro tun können, ist diese Art der Rechteckbearbeitung dennoch möglich ziemlich schön zu wissen über ...

Mike Stone
quelle