tail -f, insérer un saut de ligne après que le journal est inactif pendant 3 secondes?

14

Lorsque vous faites un tail -f error.log, comment insérer par programmation un saut de ligne après que rien n'a été ajouté au fichier pendant 3 secondes?

(évidemment, une fois qu'un saut de ligne a été ajouté, aucun autre saut de ligne ne doit être ajouté jusqu'à ce que d'autres lignes de texte soient ajoutées au fichier journal)

Par exemple, ces lignes sont ajoutées à error.log:

foo
bar
boo [[wait 4 seconds]]
2far
2foo
2bar
2boo [[wait 40 seconds]]
2far

Ce serait la sortie dans la console:

foo
bar
boo

2far
2foo
2bar
2boo

2far
Cédric
la source
Vous pourriez probablement adapter ma fonction dans askubuntu.com/a/993821/158442 , ou utiliser tspour ajouter un horodatage à la sortie et traiter les horodatages
muru
1
Il convient de mentionner que si vous le faites de manière interactive, vous pouvez simplement appuyer sur la touche Entrée plusieurs fois. :)
Wildcard

Réponses:

12

Vous pouvez toujours implémenter le tail -f(enfin ici, à moins que vous ne décommentiez le seek(), plus comme tail -n +1 -fnous vidons tout le fichier) à la main avec perlpar exemple:

perl -e '
  $| = 1;
  # seek STDIN, 0, 2; # uncomment if you want to skip the text that is
                      # already there. Or if using the ksh93 shell, add
                      # a <((EOF)) after < your-file
  while (1) {
    if ($_ = <STDIN>) {
      print; $t = 0
    } else {
      print "\n"            if $t == 3;
      # and a line of "-"s after 10 seconds:
      print "-" x 72 . "\n" if $t == 10;
      sleep 1;
      $t++;
    }
  }' < your-file

Ou laissez tail -ffaire la queue et utilisez perlpour insérer les sauts de ligne s'il n'y a pas d'entrée pendant 3 secondes:

tail -f file | perl -pe 'BEGIN{$SIG{ALRM} = sub {print "\n"}} alarm 3'

Ceux-ci supposent que la sortie elle-même n'est pas ralentie (comme lorsque la sortie va vers un tube qui n'est pas activement lu).

Stéphane Chazelas
la source
Il m'a fallu beaucoup de temps pour comprendre pourquoi le second fonctionne réellement :)
hobbs
J'ai essayé le premier, et il a imprimé TOUS les fichiers avant, donc ce n'est pas optimal. Le second fonctionne comme un charme. J'ai ajouté le "tail -n 0 -f $ 1 |" option (-n 0) pour éviter d'afficher les anciennes lignes de fichiers.
Cedric
Petite question: comment modifier la deuxième solution pour afficher une ligne supplémentaire de tirets (-------) après 10 secondes? (J'ai essayé plusieurs façons, mais je ne peux rien faire fonctionner)
Cedric
1
@Cedric, voir modifier pour votre premier point. Votre deuxième exigence serait plus facile avec la première approche.
Stéphane Chazelas
8

bash+ datesolution:

while IFS= read -r line; do        
    prev=$t         # get previous timestamp value
    t=$(date +%s)   # get current timestamp value
    [[ ! -z "$prev" ]] && [[ "$((t-prev))" -ge 3 ]] && echo ""
    echo "$line"    # print current line
done < <(tail -f error.log)
RomanPerekhrest
la source
Dans Bash, vous pouvez utiliser $SECONDSpour compter les intervalles de temps. Je pense que c'est le nombre de secondes écoulées depuis le démarrage de la coque, pas que cela soit important pour prendre une différence.
ilkkachu
@ilkkachu, ou read -tou $TMOUT. $SECONDSest cassé bashet mksh. time bash -c 'while ((SECONDS < 3)); do :; done'durera entre 2 et 3 secondes. Mieux vaut utiliser zsh ou ksh93 à la place ici (avec typeset -F SECONDS)
Stéphane Chazelas
@ StéphaneChazelas, je ne pense pas que ce soit différent de l'utilisation date +%s. Les deux donnent le temps en secondes complètes, ce qui a pour effet que l'intervalle de, disons, 1,9 à 4,0 ressemble à 3 secondes complètes, même si c'est vraiment 2,1. Il est difficile de contourner cela si vous ne pouvez pas accéder à la fraction de seconde. Mais oui, ils devraient probablement dormir ici au lieu de se déplacer, et read -tpourraient alors tout aussi bien être utilisés. Même si vous dormez manuellement, cela time bash -c 'while [[ $SECONDS -lt 3 ]]; do sleep 1; done'fonctionne très bien.
ilkkachu
1
ksh93 et ​​zsh sont OK avec ça (zsh ne le faisait pas). Même avec un entier $ SECONDS, le réglage SECONDS=0garantit qu'il $SECONDSatteindra 1 en 1 seconde exactement. Ce n'est pas le cas bashcar il utilise time()pour suivre $SECONDSau lieu de gettimeofday(). J'ai signalé des bogues à mksh, zsh et bash il y a quelque temps, seul zsh a été corrigé. (bon point sur le problème étant le même avec date +%s). Notez que ce n'est pas un busyloop ici, comme nous le lisons à partir de la sortie d' tail -fun tube.
Stéphane Chazelas
+1 et Bash a un « raccourci » en utilisant le haut- printfÉmuler datesans outils externes ou substitution de commande: printf -v t '%(%s)T' -1.
David Foerster
6

Pythonsolution (avec argument d' écart de temps dynamique ):

tailing_by_time.py scénario:

import time, sys

t_gap = int(sys.argv[1])    # time gap argument
ts = 0
while True:
    line = sys.stdin.readline().strip()    # get/read current line from stdin
    curr_ts = time.time()                  # get current timestamp
    if ts and curr_ts - ts >= t_gap:
        print("")                          # print empty line/newline
    ts = curr_ts
    if line:
        print(line)                        # print current line if it's not empty

Usage:

tail -f error.log | python tailing_by_time.py 3
RomanPerekhrest
la source