Não é possível redirecionar a saída de corte

3

Quando tento redirecionar a saída de cut , sempre parece estar vazio. Se não redirecionar, a saída será mostrada no terminal como esperado. Isto é verdade para o OS X 10.10 e o Linux 4.1.6.

Isso funciona:

root@karla:~# nc 10.0.2.56 30003
[...] lots of lines [...]

Isso funciona:

root@karla:~# nc 10.0.2.56 30003 | cat
[...] lots of lines [...]

Isso funciona:

root@karla:~# nc 10.0.2.56 30003 | cut -d, -f 15,16
[...] lots of lines [...]

Isso não

root@karla:~# nc 10.0.2.56 30003 | cut -d, -f 15,16 | cat
[nothing]

Isso novamente NÃO

root@karla:~# cat messung1 | cut -d, -f15,16 | cat
[...] lots of lines [...]

Isso não está limitado a cat após cut . grep , tee e redirecionamento padrão usando > também não funcionam.

O que há de errado aí?

    
por Max Ried 31.08.2015 / 17:39

1 resposta

9

Não é tanto que não haja saída, pois está chegando em blocos.

Como muitos programas, quando sua saída não é mais um terminal, cut armazena em buffer sua saída. Ou seja, ele grava apenas dados quando acumulou um buffer cheio dele. Normalmente, algo como 4 ou 8 kiB, embora YMMV.

Você pode verificar isso facilmente comparando:

(echo foo; sleep 1; echo bar) | cut -c2-

Com:

(echo foo; sleep 1; echo bar) | cut -c2- | cat

No primeiro caso, cut ouputs oo\n e ar\n um segundo depois, enquanto no segundo caso, corte as saídas oo\nar\n após 1 segundo, ou seja, quando ele vê o final de sua entrada e libera sua saída ao sair.

No seu caso, como stdin é nc , ele só veria o final de sua entrada quando a conexão é fechada, portanto, ele só iniciaria a saída de qualquer coisa depois de acumular 4kiB de dados para gravar.

Para contornar isso, várias abordagens são possíveis.

  • Em sistemas GNU ou FreeBSD, você pode usar o utilitário stdbuf que pode ajustar o comportamento de buffer de alguns comandos (ele não funciona para todos, pois usa um hack LD_PRELOAD para pré-configurar o comportamento do stdio buffering).

    ... | stdbuf -oL cut -d, -f15,16 | cat
    

    diria cut para fazer um buffer baseado em linha em seu stdout.

  • alguns comandos como o GNU grep possuem opções para afetar o buffer. ( --line-buffered no caso do GNU grep ).

  • você pode usar um empacotador pseudo-tty para forçar o stdout de um comando a ser um terminal. No entanto, a maioria dessas soluções tem algumas desvantagens e limitações. O script unbuffer expect , por exemplo, frequentemente mencionado para resolver esse tipo de problema, possui vários bugs, por exemplo.

    Um que não funciona é quando usamos socat como:

    ... | socat -u 'exec:"cut -d, -f15,16",pty,raw' -
    
  • Você pode substituir seu utilitário de texto por uma ferramenta de processamento de texto de alto nível que ofereça suporte a saída sem buffer.

    • O GNU awk por exemplo tem uma função fflush() para liberar sua saída. Então, seu cut -d, -f15,16 poderia ser escrito:

      awk -F, -vOFS=, '{print $15,$16;fflush()}'
      
    • se o seu awk não tiver a função fflush() , você poderá usar system("") . Normalmente é o comando para executar um comando. Aqui, estaríamos executando um comando vazio, mas realmente usando-o para o fato de que awk libera seu stdout antes de executar o comando.

    • ou você pode usar perl :

      perl -F, -lane 'BEGIN{$,=",";$|=1} print @F[14..15]'
      
por 31.08.2015 / 22:20