Piping con Moreutils ts


9

Ho un flusso in entrata su una porta seriale, con nuove linee che appaiono circa una volta al secondo

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

Voglio eliminare le righe vuote e timestamp il resto.

sed eliminerà le righe vuote e aggiungerà un timestamp, ma non riesco a fare l'aggiornamento del timestamp, riporta solo l'ora in cui è stato invocato:

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

Ho trovato ts, parte di Moreutils, e posso convogliarlo per ottenere un timestamp di aggiornamento.

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

Tuttavia, non riesco a combinare correttamente ts con sed.

Questo, che sembra che dovrebbe fare quello che voglio, non produce alcun risultato

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

Tuttavia, invertire l'ordine dei tubi produce un output, ma ovviamente non spoglia le linee che non sono più vuote. Altre sostituzioni funzionano bene, quindi so che la pipa su sed funziona.

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

Quindi sono un po 'sconcertato. Posso presumere che sed rimuova le linee indesiderate, ma il timestamp prima della rimozione deve essere l'approccio sbagliato.

Gradirei una spiegazione e qualche aiuto.

Risposte:


9

Per rispondere direttamente alla domanda, sedè buffering e questo è l'unico problema.
Puoi risolverlo dicendogli di non bufferizzare con il suo -u/ --unbufferedflag:

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

Con un cablaggio di prova (ma è necessario eseguirlo per la prova):

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

Puoi imbatterti in situazioni simili con altri editor di stream. A quanto pare tutti vogliono bufferizzare un po '. Tutti però hanno soluzioni alternative. Ecco un sacco di comandi che ho testato:

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

Un'altra idea è di lasciarlo gawkgestire. Può filtrare le righe non vuote ed eseguire la stampa della data per te (grazie a SO's Kieron ):

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

Questo arrossisce subito dopo l'arrivo delle righe. gawkÈ particolarmente utile qui se vuoi fare altre cose ... Se vuoi controllare che la quarta colonna di output (pre ts) corrisponda a una regex, puoi (ad esempio $4~/\d{4}/). Awk (e le sue varianti) sono molto flessibili per l'elaborazione in streaming.

Un altro cablaggio di prova:

$ 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

1
+1 per sed -u. Si tratta di un problema di buffering del blocco rispetto al buffering della linea.
jfs

@Oli sed -u funziona anche perfettamente se convogliato in ts, quindi leggerò sul buffering. Non sono più sconcertato, molte grazie.
perplesso

awk è particolarmente adatto per cose come questa. il codice awk è generalmente molto meno denso e molto più leggibile di sed e puoi inserire tutte le istruzioni di stampa che vuoi visualizzare i risultati parziali durante il debug. Puoi mettere un intero programma awk in un documento qui per evitare di usare un file separato e se metti le virgolette intorno alla stringa del terminatore del documento qui, allora bash ignorerà tutti i token incorporati che normalmente proverebbe a interpretare.
Joe,

0

bash può gestirlo in un while readciclo

(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

Puoi rimuovere le linee con solo spazi bianchi con un'espansione dei parametri complicata: rimuovi tutti gli spazi bianchi iniziali e vedi se la linea è vuota:

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

Ho provato una varietà di approcci del genere, nessuno di loro ha funzionato. Non riesco nemmeno a far funzionare il tuo codice. L'uso di echo o cat per inviare / dev / ttyUSB0 al ciclo while comporta solo una singola riga di output: 2014-05-14 12:23:32 line
perplesso

Sono sicuro che c'è un modo migliore, ma prova tail -f /dev/ttyUSB0invece di cat o echo. Continuerà a funzionare. Non sapevo come testarlo sul mio sistema.
Joe,

tail -f / dev / ttyUSB0 non fornisce output, con o senza il ciclo while. tvm per i tuoi commenti.
perplesso
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.