Tuyauterie avec Moreutils ts

9

J'ai un flux entrant sur un port série, avec de nouvelles lignes apparaissant environ une fois par seconde

wren@Raven:~$ cat /dev/ttyUSB0

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

Je souhaite supprimer les lignes vides et horodater le reste.

sed supprimera les lignes vides et ajoutera un horodatage, mais je ne peux pas faire la mise à jour de l'horodatage, il indique simplement l'heure à laquelle il a été invoqué:

wren@Raven:~$ cat /dev/ttyUSB0 | sed -e '/^$/d' -e "s/$/`date +\,%F\,%T`/"
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
^C

J'ai trouvé ts, une partie de Moreutils, et je peux y accéder pour obtenir un horodatage de mise à jour.

wren@Raven:~$ cat /dev/ttyUSB0 |  ts
May 14 09:49:26 A_Sensor1,B_22.00,C_50.00
May 14 09:49:26
May 14 09:49:27 A_Sensor1,B_22.00,C_50.00
^C

Cependant, je ne peux pas combiner correctement ts avec sed.

Cela, qui semble faire ce que je veux, ne produit aucune sortie

wren@Raven:~$ cat /dev/ttyUSB0 | sed -e '/^$/d' | ts
^C
wren@Raven:~$

Cependant, inverser l'ordre des tuyaux produit une sortie, mais bien sûr, ne supprime pas les lignes qui ne sont plus vierges. D'autres substitutions fonctionnent bien, donc je sais que le tuyau à sed fonctionne.

wren@Raven:~$ cat /dev/ttyUSB0 |  ts | sed -e '/^$/d'
May 14 10:07:25 A_Sensor1,B_22.00,C_50.00
May 14 10:07:25
May 14 10:07:26 A_Sensor1,B_22.00,C_50.00
May 14 10:07:26
^C

Je suis donc un peu déconcerté. Je peux probablement faire en sorte que sed supprime les lignes indésirables, mais l'horodatage avant la suppression doit être la mauvaise approche.

J'apprécierais une explication et un peu d'aide.

perplexe
la source

Réponses:

9

Répondre directement à la question, sedc'est mettre en mémoire tampon et c'est le seul problème.
Vous pouvez résoudre ce problème en lui disant de ne pas mettre en mémoire tampon avec son indicateur -u/ --unbuffered:

sed -u '/^$/d' /dev/ttyUSB0 | ts

Avec un harnais de test (mais vous devrez l'exécuter pour preuve):

$ (echo -e 'banana\n\n'; sleep 2; echo 'cheese') | sed -u '/^$/d' | ts
May 14 11:26:05 banana
May 14 11:26:07 cheese

Vous pouvez rencontrer des difficultés similaires avec d'autres éditeurs de flux. Apparemment, ils veulent tous tamponner un peu. Cependant, ils ont tous des solutions de contournement. Voici un tas de commandes que j'ai testées:

... | mawk -W interactive '/./' | ts
... | gawk '/./ { print $0; fflush(); }' | ts
... | grep --line-buffered '.' | ts
... | perl -n -e 'print if /./' | ts

Une autre idée est de laisser le gawkgérer. Il peut filtrer les lignes non vides et faire l'impression de la date pour vous (grâce à Kieron de SO ):

awk '/./ { print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }' /dev/ttyUSB0

Cela s'écoule directement après l'entrée des lignes. gawkEst particulièrement utile ici si vous voulez faire autre chose ... Si vous voulez vérifier que la quatrième colonne de sortie (pré- ts) correspond à une expression régulière, vous pouvez (par exemple $4~/\d{4}/). Awk (et ses variantes) sont très flexibles pour le traitement de flux.

Un autre harnais de test:

$ gawk '/./ { print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }' <(
      echo -e 'banana\n\n';
      sleep 2;
      echo 'cheese'
  )
2014-05-14 11:13:59 banana
2014-05-14 11:14:01 cheese
Oli
la source
1
+1 pour sed -u. Il s'agit d'un problème de mise en mémoire tampon de bloc par rapport à la mise en mémoire tampon de ligne.
jfs
@Oli sed -u fonctionne également parfaitement lorsqu'il est canalisé dans ts, je vais donc vous renseigner sur la mise en mémoire tampon. Je ne suis plus dérouté, merci beaucoup.
perplexe
awk est particulièrement bien adapté pour des choses comme ça. Le code awk est généralement beaucoup moins dense et beaucoup plus lisible que sed et vous pouvez ajouter autant d'instructions d'impression que vous le souhaitez pour afficher des résultats partiels lors du débogage. Vous pouvez mettre un programme awk entier dans un document ici pour éviter d'utiliser un fichier séparé et si vous mettez des guillemets autour de la chaîne de terminaison du document ici, alors bash ignorera tous les jetons incorporés qu'il essaierait normalement d'interpréter.
Joe
0

bash peut gérer cela en while readboucle

(echo -e 'banana\n\n'; sleep 2; echo 'cheese') | 
while IFS= read -r line; do 
    [[ $line ]] && echo "$(date "+%F %T") line"
done
2014-05-14 06:34:06 banana
2014-05-14 06:34:08 cheese

Vous pouvez supprimer des lignes avec uniquement des espaces blancs avec une expansion de paramètre délicate: supprimez tous les espaces blancs de début et voyez si la ligne est vide:

shopt -s extglob

(echo -e '  banana\n\t\n'; sleep 2; echo 'cheese') |
while IFS= read -r line; do
    [[ "${line/#+([[:blank:]])/}" ]] && echo "$(date "+%F %T") $line"
done
glenn jackman
la source
J'ai essayé une variété d'approches comme ça, aucune d'entre elles n'a fonctionné. Je ne peux pas non plus faire fonctionner votre code. L'utilisation d'écho ou de cat pour envoyer / dev / ttyUSB0 à la boucle while ne fait que produire une seule ligne de sortie: 2014-05-14 12:23:32 line
perplexed
Je suis sûr qu'il existe un meilleur moyen, mais essayez tail -f /dev/ttyUSB0plutôt que chat ou écho. Il continuera de fonctionner. Je ne savais pas comment tester cela sur mon système.
Joe
tail -f / dev / ttyUSB0 ne donne aucune sortie, avec ou sans la boucle while. tvm pour vos commentaires.
perplexe