A questão da jq
necessidade de um filtro explícito quando a saída é redirecionada é discutida em toda a web. Mas não consigo redirecionar a saída se fizer jq
parte de uma cadeia de pipe, mesmo quando um filtro explícito está em uso.
Considerar:
touch in.txt
tail -f in.txt | jq '.f1'
# in a different terminal:
echo '{"f1":1,"f2":2}' >> in.txt
echo '{"f1":3,"f2":2}' >> in.txt
Como esperado, a saída no terminal original do jq
comando é:
1
3
Mas se eu adicionar qualquer tipo de redirecionamento ou canalização ao final do jq
comando, a saída ficará silenciosa:
rm in.txt
touch in.txt
tail -f in.txt | jq '.f1' | tee out.txt
# in a different terminal:
echo '{"f1":1,"f2":2}' >> in.txt
echo '{"f1":3,"f2":2}' >> in.txt
Nenhuma saída aparece no primeiro terminal e out.txt está vazio.
Eu tentei centenas de variações, mas é um problema indescritível. A única solução alternativa que encontrei , conforme descoberto por meio de mosquitto_sub
e The Things Network (que foi onde também descobri o problema), é agrupar as funções tail e jq em um script de shell:
#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done
Então:
./tail_and_jq.sh | tee out.txt
# in a different terminal:
echo '{"f1":1,"f2":2}' >> in.txt
echo '{"f1":3,"f2":2}' >> in.txt
E com certeza, a saída aparece:
1
3
Isso com o último jq
instalado via Homebrew:
$ echo $SHELL
/bin/bash
$ jq --version
jq-1.5
$ brew install jq
Warning: jq 1.5_3 is already installed and up-to-date
Isso é um bug (em grande parte não documentado) no jq
meu entendimento de correntes de tubulação?
A saída de
jq
é armazenada em buffer quando sua saída padrão é canalizada.Para solicitar que
jq
limpe seu buffer de saída após cada objeto, use sua--unbuffered
opção, por exemploDo
jq
manual:O que você está vendo aqui é o buffer C stdio em ação. Ele armazenará a saída em um buffer até atingir um determinado limite (pode ser 512 bytes ou 4 KB ou maior) e, em seguida, enviará tudo de uma vez.
Esse buffer é desabilitado automaticamente se stdout estiver conectado a um terminal, mas quando estiver conectado a um pipe (como no seu caso), ele habilitará esse comportamento de buffer.
A maneira usual de desabilitar/controlar o buffer é usando a
setvbuf()
função (veja esta resposta para mais detalhes), mas isso precisaria ser feito no próprio código-fontejq
, então talvez não seja algo prático para você ...Existe uma solução alternativa ... (Um hack, pode-se dizer.) Existe um programa chamado "unbuffer", distribuído com "expect" que pode criar um pseudo-terminal e conectá-lo a um programa. Portanto, mesmo que
jq
ainda esteja gravando em um canal, ele pensará que está gravando em um terminal e o efeito de buffer será desativado.Instale o pacote "expect", que deve vir com "unbuffer", caso ainda não o tenha... Por exemplo, no Debian (ou Ubuntu):
Então você pode usar este comando:
Veja também esta resposta para mais detalhes sobre "unbuffer", e você também pode encontrar uma página de manual aqui .