Réponses:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Comment ça fonctionne:
cat
lit le fichier appelé input.log
et l'imprime simplement dans son flux de sortie standard.
Normalement, la sortie standard est connectée à un terminal, mais ce petit script contient |
donc le shell redirige la sortie standard de cat
vers l'entrée standard de sed
.
sed
lit les données (au fur et à mesure cat
qu'elles les produisent), les traite (selon le script fourni avec l' -e
option), puis les imprime sur sa sortie standard. Le script "s/^/$(date -R) /"
signifie remplacer chaque début de ligne par un texte généré par la date -R
commande (la construction générale de la commande replace est:) s/pattern/replace/
.
Ensuite, selon >>
bash
redirige la sortie de sed
vers un fichier appelé output.log
( >
signifie remplacer le contenu du fichier et >>
signifie ajouter à la fin).
Le problème est $(date -R)
évalué une fois lorsque vous exécutez le script, il insérera donc l' horodatage actuel au début de chaque ligne. L'horodatage actuel peut être loin d'un moment où un message a été généré. Pour l'éviter, vous devez traiter les messages au fur et à mesure qu'ils sont écrits dans le fichier, et non avec une tâche cron.
La redirection de flux standard décrite ci-dessus s'appelle pipe . Vous pouvez le rediriger non seulement avec |
entre les commandes du script, mais via un fichier FIFO (alias nommé pipe ). Un programme écrit dans le fichier et un autre lit les données et les reçoit lors des premiers envois.
Choisissez un exemple:
$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;
# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo
$ cat foo.log
Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz
Comment ça fonctionne:
mkfifo
crée un tube nommé
while true; do sed ... ; done
exécute une boucle infinie et à chaque itération, elle s'exécute sed
avec redirection foo.log.fifo
vers son entrée standard; sed
bloque l'attente des données d'entrée , puis traite un message reçu et l'imprime sur la sortie standard redirigée vers foo.log
.
À ce stade, vous devez ouvrir une nouvelle fenêtre de terminal car la boucle occupe le terminal actuel.
echo ... > foo.log.fifo
imprime un message sur sa sortie standard redirigée vers le fichier fifo et le sed
reçoit et le traite et l'écrit dans un fichier normal.
La note importante est le fifo, tout comme n'importe quel autre tuyau n'a aucun sens si l'un de ses côtés n'est connecté à aucun processus. Si vous essayez d'écrire dans un tuyau, le processus en cours se bloquera jusqu'à ce que quelqu'un lise les données de l'autre côté du tuyau. Si vous souhaitez lire à partir d'un canal, le processus se bloquera jusqu'à ce que quelqu'un écrive des données dans le canal. La sed
boucle de l'exemple ci-dessus ne fait rien (s'endort) jusqu'à ce que vous le fassiez echo
.
Pour votre situation particulière, il vous suffit de configurer votre application pour écrire des messages de journal dans le fichier fifo. Si vous ne pouvez pas le configurer, supprimez simplement le fichier journal d'origine et créez un fichier fifo. Mais notez à nouveau que si la sed
boucle meurt pour une raison quelconque - votre programme sera bloqué lors de la tentative d'accès au write
fichier jusqu'à ce que quelqu'un read
vienne du fifo.
L'avantage est l' horodatage actuel évalué et joint à un message pendant que le programme l'écrit dans le fichier.
tailf
Pour rendre l'écriture dans le journal et le traitement plus indépendants, vous pouvez utiliser deux fichiers standard avec tailf
. Une application écrira un message dans un fichier brut et un autre processus lira de nouvelles lignes (suivez pour écrire de manière asynchrone) et traitera les données avec l'écriture dans le deuxième fichier.
Prenons un exemple:
# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;
$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log
$ cat bar.log
Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz
Comment ça fonctionne:
Exécutez le tailf
processus qui suivra les écritures bar.raw.log
et imprimez-les sur la sortie standard redirigée vers la while read ... echo
boucle infinie . Cette boucle effectue deux actions: lire les données de l'entrée standard dans une variable tampon appelée line
puis écrire l'horodatage généré avec les données tamponnées suivantes dans le bar.log
.
Écrivez quelques messages au bar.raw.log
. Vous devez le faire dans une fenêtre de terminal séparée car la première sera occupée par tailf
laquelle suivra les écritures et fera son travail. Assez facile.
L'avantage est que votre application ne bloquera pas si vous tuez tailf
. Les inconvénients sont des horodatages moins précis et la duplication des fichiers journaux.
tailf
, a ajouté la bonne façon de l'utiliser. En fait, le chemin avec tailf
semble être plus élégant, mais j'ai quitté le chemin fifo avec l'espoir qu'il sera utile pour quelqu'un.
Vous pouvez utiliser le ts
script perl à partir de moreutils
:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Modifié de la réponse de Dmitry Vasilyanov.
Dans le script bash, vous pouvez rediriger et encapsuler la sortie avec l'horodatage ligne par ligne à la volée.
Quand utiliser:
tailf
le fichier journal comme l'a dit Dmitry Vasilyanov.Un exemple nommé foo.sh
:
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"
Et le résultat:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Comment ça fonctionne
exec &>
Rediriger stdout et stderr au même endroit>( ... )
diriger les sorties vers une commande interne asynchronePar exemple:
horodatage du tube et journal dans un fichier
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
Ou imprimez l'horodatage et connectez-vous à stdout
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
puis enregistrez-les dans le /etc/crontab
réglage
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
J'ai utilisé ts
cette méthode pour obtenir une entrée avec un horodatage dans un journal d'erreurs pour un script que j'utilise pour obtenir Cacti rempli de statistiques d'un hôte distant.
Pour tester Cacti, j'utilise rand
pour ajouter des valeurs aléatoires que j'utilise pour les graphiques de température pour surveiller la température de mon système.
Pushmonstats.sh est un script qui collecte les statistiques de température du système de mon PC et les envoie à un Raspberry Pi sur lequel Cacti s'exécute. Il y a quelque temps, le réseau était bloqué. J'ai seulement obtenu des délais d'expiration SSH dans mon journal d'erreurs. Malheureusement, aucune entrée d'heure dans ce journal. Je ne savais pas comment ajouter un horodatage à une entrée de journal. Donc, après quelques recherches sur Internet, je suis tombé sur ce post et c'est ce que j'ai fait en utilisant ts
.
Pour le tester, j'ai utilisé une option inconnue pour rand
. Ce qui a donné une erreur à stderr. Pour le capturer, je le redirige vers un fichier temporaire. Ensuite, j'utilise cat pour afficher le contenu du fichier et le rediriger vers ts
, ajouter un format d'heure que j'ai trouvé sur ce post et enfin l'enregistrer dans le fichier d'erreur. Ensuite, j'efface le contenu du fichier temporaire, sinon j'obtiens des entrées doubles pour la même erreur.
Crontab:
* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err
Cela donne ce qui suit dans mon journal des erreurs:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Ce n'est peut-être pas une façon très élégante de le faire, mais cela fonctionne. Je me demande s'il existe une approche plus élégante.