Pré-ajouter un horodatage à chaque ligne de sortie dune commande

Je souhaite ajouter un horodatage à chaque ligne de sortie dune commande. Par exemple:

foo bar baz 

deviendrait

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

… où lheure est le préfixe est lheure à laquelle la ligne a été imprimée. Comment puis-je y parvenir?

Commentaires

Réponse

moreutils inclut ts qui fait cela très bien:

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

Il élimine également le besoin dune boucle, chaque ligne de sortie aura un horodatage.

$ 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 

Vous voulez savoir quand ce serveur est revenu, vous avez redémarré? Exécutez simplement ping | ts, problème résolu: D.

Commentaires

  • Comment nai-je pas su à ce sujet ?!?!?! Cela complète étonnamment tail -f! tail -f /tmp/script.results.txt | ts
  • si je nai ‘ pas la commande ts, que dois-je utiliser?
  • Si ‘ ne fonctionne pas, essayez de rediriger stderr vers stdout, par exemple ssh -v 127.0.0.1 2>&1 | ts
  • Installer en faisant sudo apt install moreutils sur Debian et yum install moreutils sur Fedora.
  • Je pense que souligner le paramètre -s est utile. Comme cela affiche le temps dexécution de la commande. Personnellement, jaime utiliser à la fois ts et ts -s en même temps. Ressemble à quelque chose comme ceci: command | ts -s '(%H:%M:%.S)]' | ts '[%Y-%m-%d %H:%M:%S'. Ceci ajoute les lignes de journal comme ceci: [2018-12-04 08:31:00 (00:26:28.267126)] Hai <3

Answer

Premièrement, si vous vous attendez à ce que ces horodatages représentent réellement un événement, gardez à lesprit que, puisque de nombreux programmes effectuent une mise en mémoire tampon de ligne (certains plus agressivement que dautres), il est important de penser à cela comme proche de lheure à laquelle la ligne dorigine ont été imprimés plutôt que l’horodatage d’une action en cours.

Vous pouvez également vérifier que votre commande n’a pas déjà une fonction intégrée dédiée à cette opération. Par exemple, ping -D existe dans certaines versions de ping et affiche lheure depuis lépoque Unix avant chaque ligne. Si votre commande ne contient pas sa propre méthode, sont quelques méthodes et outils qui peuvent être utilisés, entre autres:

Shell POSIX

Gardez à lesprit que comme de nombreux shells stockent leurs chaînes en interne sous forme de chaînes de caractères, si lentrée contient la valeur null carboniser acter (\0), cela peut entraîner la fin prématurée de la ligne.

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] ")" 

Commentaires

  • Un problème ici est que de nombreux programmes s’activent encore plus de mise en mémoire tampon de sortie lorsque leur sortie standard est un tube au lieu du terminal.
  • @cjm – True. Une certaine mise en mémoire tampon de sortie peut être allégée en utilisant stdbuf -o 0, mais si le programme gère manuellement sa mise en mémoire tampon de sortie, il n’a pas ‘ t aider (sauf si il existe une option pour désactiver / réduire la taille du tampon de sortie).
  • Pour python, vous pouvez désactiver la mise en mémoire tampon de ligne avec python -u
  • @Bwmat No. ... for x in sys.stdin parcourt les lignes sans les mettre toutes en mémoire tampon en premier.
  • Faites ceci et vous obtenez une mise en mémoire tampon … pour un in 1 1 1 1 1; dormir 1; écho; fait | python -c ‘ import sys, heure; sys.stdout.write ( »  » .join (( »  » .join ((time.strftime ( » [% Y-% m-% d% H:% M:% S] « , time.gmtime ()), ligne)) pour la ligne dans sys.stdin))) ‘

Réponse

Pour une mesure delta ligne par ligne , essayez gnomon .

Cest un utilitaire de ligne de commande, un peu comme moreutils « s ts, pour ajouter des informations dhorodatage à la sortie standard dune autre commande. Utile pour les processus de longue durée où vous » voudriez un enregistrement historique de ce qui prend si longtemps.

Tout acheminer vers gnomon sera ajoutez un horodatage à chaque ligne, indiquant combien de temps cette ligne était la dernière ligne du tampon, cest-à-dire combien de temps il a fallu à la ligne suivante pour apparaître. Par défaut, gnomon affichera les secondes écoulées entre chaque ligne, mais cest configurable.

Démo gnomon

Commentaires

  • Cela ressemble à une excellente alternative à ts lors de lutilisation de processus en direct. Alors que ts convient mieux aux processus non interactifs.

Réponse

Jaurais préféré commenter ci-dessus, mais je ne peux pas, de manière réputée. Quoi quil en soit, lexemple Perl ci-dessus peut être sans tampon comme suit:

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

Le le premier « $ | » supprime STDOUT. Le second définit stderr comme canal de sortie par défaut actuel et le déprime. Puisque select retourne le paramètre dorigine de $ |, en enveloppant le select dans un select, nous remettons également $ | à sa valeur par défaut, STDOUT.

Et oui, vous pouvez couper « n coller tel quel ». Je lai multi-ligné pour plus de lisibilité.

Et si vous voulez vraiment être précis (et que vous avez installé Time :: Hires ):

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

Commentaires

  • Fonctionne comme un charme, sans avoir à installer de packages non standard.

Réponse

Message de Ryan fournit une idée intéressante, mais elle échoue à plusieurs égards. Lors des tests avec tail -f /var/log/syslog | xargs -L 1 echo $(date +"[%Y-%m-%d %H:%M:%S]") $1, jai remarqué que lhorodatage reste le même même si stdout vient plus tard avec une différence en secondes dintervalle. Considérez cette sortie:

[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. 

Ma solution proposée est similaire, mais fournit un horodatage approprié et utilise un peu plus portable printf plutôt que echo

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

Pourquoi bash -c "..." bash? Parce quen raison de loption -c, premier argument nt est assigné à $0 et napparaîtra pas dans la sortie. Consultez la page de manuel de votre shell pour la description correcte de -c

Test de cette solution avec tail -f /var/log/syslog et (comme vous pourriez probablement deviner) la déconnexion et la reconnexion à mon wifi, a montré lhorodatage correct fourni par les messages date et syslog

Bash peut être remplacé par nimporte quel shell de type bourne, peut être fait avec ksh ou dash, au moins ceux qui ont loption -c.

Problèmes potentiels:

La solution nécessite davoir xargs, qui est disponible sur les systèmes compatibles POSIX, donc la plupart des systèmes de type Unix devraient être couverts. Évidemment, cela ne fonctionnera pas si votre système nest pas conforme à POSIX ou na pas GNU findutils

Réponse

La plupart des réponses suggèrent dutiliser date, mais cest assez lent . Si votre version de bash est supérieure à 4.2.0, il est préférable dutiliser printf à la place, cest une version intégrée de bash. Si vous avez besoin de prendre en charge les anciennes versions de bash, vous pouvez créer la fonction log en fonction de la version de bash:

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 

Voir la vitesse différences:

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: au lieu de $(date +"${TIMESTAMP_FORMAT}"), il « vaut mieux utiliser $(exec date +"${TIMESTAMP_FORMAT}") ou même $(exec -c date +"${TIMESTAMP_FORMAT}") trop accélérer lexécution.

UPD2: bash 5 fournit une variable EPOCHREALTIME pour les microsecondes granularité, vous pouvez lutiliser par cette commande (environ 30% plus lente que les secondes seulement): printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

Commentaires

  • Cette réponse est très sous-estimée! Particulièrement agréable pour montrer limpact relatif sur les performances.
  • Excellente suggestion, mais malheureusement la date ne fournit pas de résolution inférieure à la seconde, ce qui est une exigence dans mon cas
  • Avec bash 5.0+, vous pouvez utiliser cette commande: printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"

Answer

Vous pouvez le faire avec date et xargs:

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

Explication:

xargs -L 1 dit à xargs dexécuter la commande en cours pour chaque ligne dentrée, et il passe dans la première ligne comme il le fait. echo `date +"[%Y-%m-%d %H:%M:%S]"` $1 fait essentiellement écho à la date avec largument dentrée à la fin de celle-ci

Commentaires

  • La solution est proche, mais ‘ t horodatage correctement quand il sagit de la sortie séparée par de longues périodes de temps. De plus, vous ‘ utilisez des contre-coupures et vous navez ‘ t quoted $1. Ce ‘ nest pas du bon style. Citez toujours les variables. De plus, vous ‘ utilisez echo, qui nest pas portable. Cela ‘ est correct, mais peut ne pas fonctionner correctement sur certains systèmes.
  • Après avoir testé ceci, il semble que vous ‘ avez tout à fait raison … connaissez-vous un moyen de faire date être réévalué à chaque ligne, ou est-ce quasiment sans espoir?

Réponse

Plug impudique pour quelque chose que je vient décrire pour résoudre ce problème exact: ets , écrit en Go.

démo

Vous pouvez trouver de nombreux exemples dutilisation sur la page du projet.

La différence notable par rapport aux réponses existantes et aux offres similaires est que ets est conçu pour exécuter votre commande à votre place, dans un pty (pseudo tty) – cest-à-dire simulant votre commande sexécutant nativement dans un tty. Par rapport à la sortie de la commande de tuyauterie par ex. ts, cela rend lhorodatage presque transparent et résout un tas de problèmes de tuyauterie:

  • Certains programmes mettent en mémoire tampon de manière agressive lors de lécriture dans un tube, donc vous ne voyez aucune sortie et puis tout un tas de sorties (ouais, vous pouvez les stdbuf, vous pouvez même envelopper stdbuf et ts dans un alias / fonction, mais ce ne serait pas mieux si les choses fonctionnent hors de la boîte);
  • Certains programmes désactivent la couleur et / ou linteractivité lors de lécriture dans un tube;
  • Le statut de sortie a disparu sauf si vous avez activé pipefail; etc.

Les commandes peuvent être directement exécutée « ed, ce qui signifie que vous pouvez simplement ajouter ets à votre ligne de commande existante, ou il peut sagir de commandes shell (comme indiqué dans le gif ci-dessus). Bien sûr, si vous souhaitez diriger la sortie vers, ets peut le faire aussi.

ets prend en charge la même chose Modes dhorodatage comme moreutils ts: mode temps absolu, mode temps écoulé et mode temps incrémental. Il utilise des valeurs par défaut plus saines (par exemple, lhorloge monotone est toujours utilisée pour les horodatages écoulés / incrémentiels) et dispose dun support supplémentaire pour les fuseaux horaires personnalisés. Il ya « une comparaison détaillée ici .

Encore une fois, https://github.com/zmwangx/ets . Essayez-le, signalez des bogues, etc.

Réponse

Mettez ceci vers le début de votre script

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

Ou, pour un crédit supplémentaire, la sortie dans le fichier journal via:

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

pour afficher à la fois la console et le fichier journal:

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

Les principaux avantages de cette opération sont de séparer la journalisation de tout le reste, non encombrer le corps du script en redirigeant vers tee ou similaire sur chaque commande, et ne pas avoir à écrire des fonctions de journalisation personnalisées et sen tenir à lancien echo.

Le programme ts se trouve dans le package moreutils, qui devrait être facilement disponible sous régime dadministration sensé. 🙂

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *