Existe-t-il un utilitaire Unix pour ajouter les horodatages à stdin?


168

J'ai fini par écrire un petit script rapide pour cela en Python, mais je me demandais s'il y avait un utilitaire dans lequel vous pourriez insérer du texte dans lequel ajouterait chaque ligne avec du texte - dans mon cas spécifique, un horodatage. Idéalement, l'utilisation serait quelque chose comme:

cat somefile.txt | prepend-timestamp

(Avant de répondre à sed, j'ai essayé ceci:

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

Mais cela n'évalue la commande de date qu'une seule fois lorsque sed est exécuté, de sorte que le même horodatage est incorrectement ajouté à chaque ligne.)


Est-ce cat somefile.txtun peu "trompeur"? Je m'attendrais à ce que cela se produise "à la fois" et ait un seul horodatage. Ne serait - ce pas un meilleur programme de test: (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2)?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:


185

Pourrait essayer d'utiliser awk:

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

Vous devrez peut-être vous assurer que <command>produit une sortie en tampon de ligne, c'est-à-dire qu'il vide son flux de sortie après chaque ligne; L'horodatage awkajouté sera l'heure à laquelle la fin de la ligne est apparue sur son tube d'entrée.

Si awk affiche des erreurs, essayez à la gawkplace.


31
Sur mon système, «awk» lui-même était en mémoire tampon. (Cela peut être problématique pour les fichiers journaux). J'ai corrigé cela en utilisant: awk '{print strftime ("% Y-% m-% dT% H:% M:% S"), $ 0; fflush (); } '
user47741

19
strftime () semble être une extension GNU awk, donc si vous êtes sous Mac OS, par exemple, utilisez gawk au lieu de awk.
Joe Shaw

Cela ne cesse de m'étonner à quel point la bash est puissante. Je ne pensais pas qu'il y aurait une solution aussi simple pour cette tâche compliquée.
recluze le

2
Pour les utilisateurs d'OS X, vous devez installer gawket utiliser cela à la place de awk. Si vous avez MacPorts:sudo port install gawk
Matt Williamson

5
@teh_senaus Autant que je sache, awk's strftime()n'a pas une précision à la milliseconde. Cependant, vous pouvez utiliser la datecommande. Fondamentalement, vous réduisez simplement les nanosecondes à trois caractères. Il ressemblera à ceci: COMMAND | while read -r line; do echo "$(date '+%Y-%m-%d %T.%3N') $line"; done. Notez que vous pouvez abréger% H:% M:% S avec% T. Si vous souhaitez toujours utiliser awkpour une raison quelconque, vous pouvez faire ce qui suit:COMMAND | awk '{ "date +%Y-%m-%d\\ %T.%3N" | getline timestamp; print timestamp, $0; fflush(); }'
Six

190

tsde moreutils ajoutera un horodatage à chaque ligne d'entrée que vous lui donnez. Vous pouvez également le formater en utilisant strftime.

$ 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
$ 

Pour l'installer:

sudo apt-get install moreutils

2
pour capturer et tamponner stderr en même temps: bad command 2>&1 | tsrésultatsJan 29 19:58:31 -bash: bad: command not found
rymo

Je souhaite utiliser le format de l'heure 2014 Mar 21 18:07:28. Comment puis-je l'obtenir?
becko

@becko - selon la page de manuel , donnez une strftimechaîne de format comme argument :, [.. command ..] | ts '%Y %b %d %T'par exemple.
Toby Speight

2
Pour OS X, brew install moreutils. Pour générer UTC, vous pouvez définir TZ localement, par exemplels -l |TZ=UTC ts '%Y-%m-%dT%H:%M:%SZ'
karmakaze

1
@Dorian c'est parce que ruby ​​met la sortie en mémoire tampon; si vous videz le tampon avant de dormir, cela fonctionne assez bien:ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
umläute

45

annoter , disponible via ce lien ou comme annotate-outputdans le devscriptspaquet Debian .

$ 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

1
C'est une excellente solution, mais ne fonctionnera pas avec une sortie canalisée ou une sortie sous-interprétée. Il ne reformatera la sortie que pour un programme ou un script que vous lui donnez comme argument.
slm le

1
annotate-output fonctionne bien et est facile à utiliser, mais est VRAIMENT lent.
Paul Brannan

31

Distiller les réponses données à la plus simple possible:

unbuffer $COMMAND | ts

Sur Ubuntu, ils proviennent des packages expect-dev et moreutils.

sudo apt-get install expect-dev moreutils

1
Ce n'est expectpas le cas expect-dev, mais le dernier tire le premier pour que cela fonctionne. (Ubuntu 14.10, je me demande si le binaire a été déplacé vers un package différent à un moment donné.)
Marius Gedminas

Pour Ubuntu 14.04 LTS, il est toujours disponibleexpect-dev
Willem

2
Si vous souhaitez obtenir le temps écoulé avec une précision de la milliseconde, utilisez unbuffer $COMMAND | ts -s %.s( -ssi pour le temps écoulé et %.spour le temps en secondes avec partie fractionnaire)
Greg Witczak

24

Que dis-tu de ça?

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

À en juger par votre désir d'obtenir des horodatages en direct, vous souhaitez peut-être effectuer une mise à jour en direct sur un fichier journal ou quelque chose? Peut être

tail -f /path/to/log | perl -pne 'print scalar(localtime()), " ";' > /path/to/log-with-timestamps

4
Cet exemple perl m'a été particulièrement utile car je travaillais en tant qu'utilisateur sur un déploiement ubuntu, qui utilise apparemment «mawk» au lieu de «gawk» - et n'a pas la fonction strftime. Merci!
opello

4
+1 à ceci étant pratique sur une installation par défaut d'Ubuntu. J'avais l'habitude tail -f /path/to/log | perl -pne 'use POSIX qw(strftime); print strftime "%Y-%m-%dT%H:%M:%SZ ", gmtime();'d'obtenir une date de style ISO8601 / RFC3339 au lieu de la wacko que la version la plus simple donne.
natevw

1
Si vous voulez pouvoir regarder la sortie du journal horodaté à son arrivée, il peut être utile d'ajouter BEGIN { $|++; }avant l'instruction print afin que Perl vider sa sortie avec chaque ligne.

2
Vous pouvez raccourcir le script perl à juste ls | perl -pne 'print localtime." "'. La conversion scalaire se produit en raison de la concaténation de chaînes.
rahul

Pourquoi utilisez-vous les deux options -pet -n? Cela semble -nredondant si vous avez spécifié -p.
Davor Cubranic le

19

Je vais juste jeter ceci là-bas: il y a une paire d'utilitaires dans daemontools appelés tai64n et tai64nlocal qui sont faits pour ajouter des horodatages aux messages.

Exemple:

cat file | tai64n | tai64nlocal

18

La réponse de Kieron est la meilleure à ce jour. Si vous rencontrez des problèmes parce que le premier programme met en mémoire tampon, vous pouvez utiliser le programme de suppression de tampon:

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

Il est installé par défaut sur la plupart des systèmes Linux. Si vous avez besoin de le construire vous-même, il fait partie du package expect

http://expect.nist.gov


Notez que j'ai trouvé que `` unbuffer '' masque parfois le résultat de retour du programme appelé - donc ce n'est pas utile dans Jenkins ou d'autres choses qui dépendent du statut de retour
oskarpearson

10

Utilisez la commande read (1) pour lire une ligne à la fois à partir de l'entrée standard, puis sortez la ligne précédée de la date dans le format de votre choix en utilisant date (1).

$ cat timestamp
#!/bin/sh
while read line
do
  echo `date` $line
done
$ cat somefile.txt | ./timestamp

2
Un peu lourd car il forge un nouveau processus par ligne, mais ça marche!
Joe Shaw

3

Je ne suis pas un gars Unix, mais je pense que vous pouvez utiliser

gawk '{print strftime("%d/%m/%y",systime()) $0 }' < somefile.txt

3
#! /bin/sh
unbuffer "$@" | perl -e '
use Time::HiRes (gettimeofday);
while(<>) {
        ($s,$ms) = gettimeofday();
        print $s . "." . $ms . " " . $_;
}'

2

Voici ma solution awk (à partir d'un système Windows / XP avec MKS Tools installé dans le répertoire C: \ bin). Il est conçu pour ajouter la date et l'heure actuelles sous la forme mm / jj hh: mm au début de chaque ligne ayant récupéré cet horodatage du système à mesure que chaque ligne est lue. Vous pouvez, bien sûr, utiliser le modèle BEGIN pour récupérer l'horodatage une fois et ajouter cet horodatage à chaque enregistrement (tout de même). J'ai fait cela pour étiqueter un fichier journal généré sur stdout avec l'horodatage au moment où le message de journal a été généré.

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

où "pattern" est une chaîne ou une expression régulière (sans les guillemets) à mettre en correspondance dans la ligne d'entrée, et est facultatif si vous souhaitez faire correspondre toutes les lignes d'entrée.

Cela devrait également fonctionner sur les systèmes Linux / UNIX, supprimez simplement le C \: \\ bin \\ en laissant la ligne

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

Ceci, bien sûr, suppose que la commande "date" vous amène à la commande standard d'affichage / de définition de la date Linux / UNIX sans informations de chemin spécifiques (c'est-à-dire que votre variable PATH d'environnement est correctement configurée).


Sous OS XI, il fallait un espace avant la commande de date. Malheureusement, il ne semble pas réévaluer la commande pour chaque ligne. Comme d'autres l'ont suggéré, j'essaie d'ajouter des horodatages à tail -f.
Mark Bennett

2

Mélange de quelques réponses ci-dessus de natevw et Frank Ch. Eigler.

Il a des millisecondes, fonctionne mieux que d'appeler une datecommande externe à chaque fois et perl peut être trouvé dans la plupart des serveurs.

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

Version alternative avec flush et lecture en boucle:

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

1
Deux défauts avec ce qui précède: 1. $ ms n'est pas aligné et rempli de zéros. 2. Tous les codes% dans le journal seront mutilés. Ainsi au lieu de la ligne d'impression, j'ai utilisé: printf "%s+%06d %s", (strftime "%Y-%m-%dT%H:%M:%S", gmtime($s)), $ms, $_;
Simon Pickup

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

vous pouvez le faire (avec gnu / sed ):

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

exemple:

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

bien sûr, vous pouvez utiliser d'autres options de la date du programme . remplacez simplement date +%Tpar ce dont vous avez besoin.


1

La réponse de caerwyn peut être exécutée en tant que sous-programme, ce qui empêcherait les nouveaux processus par ligne:

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

echo testing 123 |timestamp

3
comment cela empêche-t-il dated'être engendré à chaque ligne?
Gyom

@Gyom Cela n'empêche pas la date d'apparaître à chaque ligne. Il empêche la coquille de se reproduire à chaque ligne. J'ajouterais que dans bash vous pouvez faire quelque chose comme: timestamp() { while read line; do echo "$(date) $line"; done; }; export -f timestampdans un script que vous source.
smbear

Appeler la date en tant que sous-processus signifie toujours la générer pour chaque ligne, mais vous lancez le script en premier lieu.
Peter Hansen

3
Pour éviter la ponte commande datepour chaque ligne, essayez d' utiliser bash printfbuiltin avec % (datespec) T format. Cela peut donc ressembler à timestamp() { while IFS= read -r line; do printf "%(%F %T)T %s\n" -1 "$line"; done; }. Les fonctions intégrées de Shell ne se reproduiront pas. Le format Datespec est comme les formats strftime(3)ie date. Cela nécessite bash, je ne sais pas depuis quelle version il prend en charge ce printfspécificateur.
Mindaugas Kubilius

1

Avertissement : la solution que je propose n'est pas un utilitaire intégré Unix.

J'ai été confronté à un problème similaire il y a quelques jours. Je n'aimais pas la syntaxe et les limites des solutions ci-dessus, j'ai donc rapidement mis en place un programme dans Go pour faire le travail à ma place.

Vous pouvez vérifier l'outil ici: preftime

Il existe des exécutables prédéfinis pour Linux, MacOS et Windows dans la section Releases du projet GitHub.

L'outil gère les lignes de sortie incomplètes et a (de mon point de vue) une syntaxe plus compacte.

<command> | preftime

Ce n'est pas idéal, mais je pensais que je le partagerais au cas où cela aiderait quelqu'un.


0

le faire avec dateet tret xargssur OSX:

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

si vous voulez des millisecondes:

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

mais notez que sur OSX, date ne vous donne pas l'option% N, vous devrez donc installer gdate ( brew install coreutils) et donc finalement arriver à ceci:

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

-1

Si la valeur que vous ajoutez est la même sur chaque ligne, lancez emacs avec le fichier, puis:

Ctrl + <space>

au début du fichier (pour marquer cet endroit), puis faites défiler jusqu'au début de la dernière ligne (Alt +> ira à la fin du fichier ... ce qui impliquera probablement la touche Shift aussi, puis Ctrl + a pour aller au début de cette ligne) et:

Ctrl + x r t

Quelle est la commande à insérer dans le rectangle que vous venez de spécifier (un rectangle de largeur 0).

2008-8-21 18:45 <enter>

Ou tout ce que vous voulez ajouter ... alors vous verrez ce texte ajouté à chaque ligne dans le rectangle de largeur 0.

MISE À JOUR: Je viens de réaliser que vous ne voulez pas la MÊME date, donc cela ne fonctionnera pas ... bien que vous puissiez le faire dans emacs avec une macro personnalisée légèrement plus compliquée, mais quand même, ce type d'édition de rectangle est assez agréable à savoir ...

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.