Tubulação com Moreutils st

8

Eu tenho um fluxo de entrada em uma porta serial, com novas linhas aparecendo cerca de uma 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

Eu quero remover as linhas em branco e marcar o tempo restante.

sed selecionará linhas em branco e adicionará um registro de data e hora, mas não posso fazer a atualização do registro de data e hora, apenas informa a hora em que foi invocado:

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

Eu encontrei ts, parte do Moreutils, e posso canalizar para obter um registro de data e hora de atualização.

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

No entanto, não consigo combinar adequadamente ts com sed.

Isso, que parece que deveria fazer o que eu quero, não produz nenhum resultado

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

No entanto, reverter a ordem dos tubos produz uma saída, mas é claro que não tira as linhas que não estão mais em branco. Outras substituições funcionam bem, então eu sei que o pipe para 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

Então estou um pouco confuso. Eu posso presumivelmente fazer sed remover as linhas indesejadas, mas o registro de data e hora antes da remoção deve ser a abordagem errada.

Gostaria de receber uma explicação e alguma ajuda.

    
por perplexed 14.05.2014 / 11:48

2 respostas

9

Para responder a pergunta diretamente, sed está armazenando em buffer e esse é o único problema. Você pode consertar isso dizendo para não armazenar em buffer com o sinalizador -u / --unbuffered :

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

Com um arnês de teste (mas você precisará executá-lo como 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

Você pode se deparar com situações semelhantes com outros editores de fluxo. Eles aparentemente todos querem amortecer um pouco. Todos eles têm soluções alternativas embora. Aqui estão alguns dos comandos que testei:

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

Outra ideia é deixar apenas gawk lidar com isso. Ele pode filtrar por linhas não vazias e fazer a impressão de data para você (graças a SO's Kieron ):

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

Isso é liberado logo após as linhas entrarem. gawk é especialmente útil aqui se você quer fazer outras coisas ... Se você quiser checar se a quarta coluna de saída (pre-%)ts) corresponde a um regex, você pode (por exemplo, $4~/\d{4}/ ). Awk (e suas variantes) são muito flexíveis para o processamento de fluxo.

Outro arsenal de testes:

$ 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
    
por Oli 14.05.2014 / 12:01
0

o bash pode lidar com isso em um while read loop

(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

Você pode remover linhas com apenas espaço em branco com uma expansão de parâmetro complicada: remova todos os espaços em branco iniciais e veja se a linha está vazia:

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
    
por glenn jackman 14.05.2014 / 12:41