Tubería con Moreutils ts

9

Tengo una transmisión entrante en un puerto serie, con nuevas líneas que aparecen aproximadamente una vez por segundo

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

Quiero eliminar líneas en blanco y marcar el tiempo del resto.

sed eliminará líneas en blanco y agregará una marca de tiempo, pero no puedo hacer la actualización de la marca de tiempo, solo informa la hora en que se invocó:

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

He encontrado ts, parte de Moreutils, y puedo canalizarlo para obtener una marca de tiempo de actualización.

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

Sin embargo, no puedo combinar adecuadamente ts con sed.

Esto, que parece que debería hacer lo que quiero, no produce ningún resultado

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

Sin embargo, invertir el orden de las tuberías produce una salida, pero por supuesto no elimina las líneas que ya no están en blanco. Otras sustituciones funcionan bien, así que sé que la tubería a sed está funcionando.

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

Así que estoy un poco desconcertado. Presumiblemente, puedo hacer que sed elimine las líneas no deseadas, pero la marca de tiempo antes de la eliminación debe ser el enfoque incorrecto.

Agradecería una explicación y algo de ayuda.

perplejo
fuente

Respuestas:

9

Para responder a la pregunta directamente, sedes el almacenamiento en búfer y ese es el único problema.
Puede solucionar esto diciéndole que no se almacene con su -u/ --unbufferedflag:

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

Con un arnés de prueba (pero deberá ejecutarlo como prueba):

$ (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

Puede encontrarse con situaciones similares con otros editores de flujo. Al parecer, todos quieren amortiguar un poco. Sin embargo, todos tienen soluciones alternativas. Aquí hay un montón de comandos que he probado:

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

Otra idea es dejarlo gawkmanejar. Puede filtrar las líneas no vacías y realizar la impresión de la fecha por usted (gracias a Kieron de SO ):

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

Eso se limpia inmediatamente después de que entran las líneas. gawkEs especialmente útil aquí si desea hacer otras cosas ... Si desea verificar que la cuarta columna de salida (pre ts) coincida con una expresión regular, puede (por ejemplo $4~/\d{4}/). Awk (y sus variantes) son muy flexibles para el procesamiento de flujo.

Otro arnés de prueba:

$ 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
fuente
1
+1 para sed -u. Es un problema de búfer de bloque frente a búfer de línea.
jfs
@Oli sed -u también funciona perfectamente cuando se canaliza a ts, así que leeré sobre el almacenamiento en búfer. Ya no estoy desconcertado, muchas gracias.
perplejo
awk es particularmente adecuado para cosas como esta. El código awk generalmente es mucho menos denso y mucho más legible que sed y puede incluir tantas declaraciones de impresión como desee para ver resultados parciales durante la depuración. Puede colocar un programa awk completo en un documento here para evitar el uso de un archivo separado y si pone comillas alrededor de la cadena de terminación del documento here, bash ignorará todos los tokens incrustados que normalmente intentaría interpretar.
Joe
0

bash puede manejar esto en un while readbucle

(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

Puede eliminar líneas con solo espacios en blanco con una complicada expansión de parámetros: elimine todos los espacios en blanco iniciales y vea si la línea está vacía:

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
fuente
Intenté una variedad de enfoques como ese, ninguno de ellos funcionó. No puedo hacer que tu código funcione tampoco. El uso de echo o cat para enviar / dev / ttyUSB0 al ciclo while solo da como resultado una sola línea de salida: 2014-05-14 12:23:32 línea
perplejo
Estoy seguro de que hay una mejor manera, pero intente en tail -f /dev/ttyUSB0lugar de gato o eco. Seguirá funcionando. No sabía cómo probar esto en mi sistema.
Joe
tail -f / dev / ttyUSB0 no da salida, con o sin el ciclo while. tvm por tus comentarios.
perplejo