Piping avec Moreutils ts

J’ai un stream 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 veux effacer les lignes vides et horodater le rest.

sed éliminera les lignes vides et appenda un horodatage, mais je ne peux pas mettre à jour l’horodatage, il indique simplement l’heure à laquelle il a été appelé:

 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, qui fait 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 et sed.

Ceci, qui semble devoir 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 un résultat, mais bien sûr, il ne supprime pas les lignes qui ne sont plus vides. Les autres substitutions fonctionnent bien, alors 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 

Donc je suis un peu dérouté. Je peux probablement faire en sorte que sed supprime les lignes indésirables, mais leur horodatage avant leur suppression doit être une mauvaise approche.

J’apprécierais une explication et de l’aide.

Pour répondre directement à la question, sed est en tampon et c’est le seul problème.
Vous pouvez résoudre ce problème en lui disant de ne pas --unbuffered de tampon -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 problèmes similaires avec d’autres éditeurs de stream. Apparemment, tous veulent tamponner un peu. Ils ont tous des solutions de contournement cependant. Voici quelques 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 gawk gé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 

gawk est particulièrement utile ici si vous voulez faire autre chose … Si vous voulez vérifier que la quasortingème colonne de sortie (pre- ts ) correspond à une regex, vous pouvez (par exemple $4~/\d{4}/ ). Awk (et ses variantes) sont très flexibles pour le traitement de stream.

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 

bash peut gérer cela dans une boucle de while read

 (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 les lignes contenant uniquement des espaces blancs avec un développement de paramètre délicat: supprimez tous les espaces blancs les plus importants et vérifiez 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