Por que o awk faz buffer completo ao ler de um pipe

23

Estou lendo de uma porta serial conectada a um dispositivo GPS enviando seqüências nmea.

Uma invocação simplificada para ilustrar meu ponto:

  $ awk '{ print $0 }' /dev/ttyPSC9 
  GPGGA,073651.000,6310.1043,N,01436.1539,E,1,07,1.0,340.2,M,33.3,M,,0000*56
  $GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39
  $GPRMC,073651.000,A,6310.1043,N,01436.1539,E,0.42,163.42,070312,,,A*67
  GPGGA,073652.000,6310.1043,N,01436.1540,E,1,07,1.0,339.2,M,33.3,M,,0000*55
  $GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39

Se eu tentar ler de um canal, o awk armazenará em buffer a entrada antes de enviá-la para o stdout.

$ cat /dev/ttyPSC9 | awk '{ print $0 }'
<long pause>
GPGGA,073651.000,6310.1043,N,01436.1539,E,1,07,1.0,340.2,M,33.3,M,,0000*56
$GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39
$GPRMC,073651.000,A,6310.1043,N,01436.1539,E,0.42,163.42,070312,,,A*67
GPGGA,073652.000,6310.1043,N,01436.1540,E,1,07,1.0,339.2,M,33.3,M,,0000*55
$GPGSA,A,3,28,22,09,27,01,19,17,,,,,,2.3,1.0,2.0*39

Como posso evitar o buffer?

Edit : Kyle Jones sugeriu que o gato está protegendo sua saída, mas isso não parece estar acontecendo:

$ strace cat /dev/ttyPSC9 | awk '{ print $0 }'
write(1, "2,"..., 2)                    = 2
read(3, "E"..., 4096)                   = 1
write(1, "E"..., 1)                     = 1
read(3, ",0"..., 4096)                  = 2

Quando penso nisso: pensei que um programa usava buffer de linha ao gravar em um terminal e "buffer regular" para todos os outros casos. Então, por que o gato não está protegendo mais? A porta serial está sinalizando EOF? Então, por que o gato não é finalizado?

Daniel Näslund
fonte
1
O BashFAQ 009 pode ser útil.
jw013
@ jw013: Obrigado pelo link, uma grande soma de como o buffer funciona no bash.
Daniel Näslund

Respostas:

10

É provável que esteja armazenando buffer no awk, não no gato. No primeiro caso, o awk acredita que é interativo porque sua entrada e saída são TTYs (mesmo que sejam TTYs diferentes - acho que o awk não está verificando isso). No segundo, a entrada é um canal, por isso é executado de maneira não interativa.

Você precisará liberar explicitamente o seu programa awk. Isso não é portátil, no entanto.

Para obter mais informações e detalhes sobre como liberar a saída, leia: http://www.gnu.org/software/gawk/manual/html_node/I_002fO-Functions.html

camh
fonte
6
Obrigada pelo esclarecimento. awk -W interactive '{print $0}'parece fazer o truque. A 'W interactiveopção está disponível na minha versão do awk (mawk 1.2), mas não sei se é uma opção padrão.
Daniel Näslund
1
@dannas -Wnão está no padrão POSIX paraawk . Não sei o que fazer se você precisar de portabilidade máxima.
jw013
Estou aceitando esta resposta, uma vez que explica por que o awk está fazendo buffer completo no meu exemplo, em vez de buffer de linha - verifica se a entrada é tty e também a saída. Eu só pensei que iria verificar a saída.
Daniel Näslund
@ jw013: Obrigado por procurar o padrão. Para mim, eu só queria entender por que o awk estava fazendo buffer completo e acho que agora.
Daniel Näslund
@dannas Posso confirmar que -W interactiveé pelo menos suportado na distribuição Ubuntu 12.04 (e presumivelmente mais recente) do awk, que é mawk.
Jason C
37

Eu sei que é uma pergunta antiga, mas uma única linha pode ajudar aqueles que vêm aqui pesquisando:

cat /dev/ttyPSC9 | awk '{ print $0; system("")}'

system("")faz o truque e é compatível com POSIX. Sistemas não-posix: cuidado.

Existe uma função mais específica fflush()que faz o mesmo, mas não está disponível nas versões mais antigas do awk.

Uma informação importante dos documentos sobre o uso de system(""):

O gawk trata esse uso da função system () como um caso especial e é inteligente o suficiente para não executar um shell (ou outro interpretador de comando) com o comando vazio. Portanto, com o gawk, esse idioma não é apenas útil, mas também eficiente.

Shrein
fonte
Isso funcionou para mim
redolent
3
Meu awknão faz nada nem fflush()nem system(""). Mas eu o gawkhonrei.
Krzysztof Jabłoński