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 tubulação, mesmo quando um filtro explícito estiver 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 o out.txt está vazio.
Eu tentei centenas de variações, mas é uma questão ilusória. A única solução alternativa que encontrei , como foi descoberta na mosquitto_sub
The Things Network (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
Isto é com o mais recente 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
ou com meu entendimento das correntes de tubulação?
fonte
tail -f
para fornecer entrada contínua a um programa etee
processar a saída. Se você ainda precisasse de uma resposta, eu sugeriria simplificar a cadeia para<in.json jq '.f1' >out.json
que você pudesse restringir o que está causando isso.tail -f logfile | grep 'foo bar' | awk ...
tail
surgiu dos esforços para quebrar o canal (execute o primeiro comando, tee e redirecione para arquivo, siga-o, canalize para o próximo comando, redirecione para arquivo, etc.) e execute-o continuamente em seções. A<
é uma boa ferramenta para se ter em mente.Respostas:
A saída de
jq
é armazenada em buffer quando sua saída padrão é canalizada.Para solicitar que
jq
libere seu buffer de saída após cada objeto, use sua--unbuffered
opção, por exemploDo
jq
manual:fonte
O que você está vendo aqui é o buffer do stdio em ação. Ele armazena a saída em um buffer até atingir um determinado limite (pode ser 512 bytes ou 4KB ou maior) e depois envia tudo de uma só vez.
Esse buffer é desativado automaticamente se o stdout estiver conectado a um terminal, mas quando estiver conectado a um pipe (como no seu caso), ele permitirá esse comportamento de buffer.
A maneira usual de desativar / controlar o buffer é usar a
setvbuf()
função (consulte esta resposta para obter mais detalhes), mas isso precisa 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.) Há um programa chamado "buffer", 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", se você ainda não o possui ... Por exemplo, no Debian (ou Ubuntu):
Então você pode usar este comando:
Veja também esta resposta para obter mais detalhes sobre "buffer", e você pode encontrar uma página de manual aqui também .
fonte
jq
implementa nativamente a saída sem buffer, para que não haja necessidade de solução alternativa.jq
página de manual, mas fiquei entediado depois de um tempo e fui fazer outras coisas ... É bom saber que há algo assim! :stdbuf -o0
quais injetam código via LD_PRELOAD e fazem asetvbuf()
chamada mágica para você. Se funciona no macOS, não tenho certeza.expect
estiver pré-instalado no macos,unbuffer
não é. No entanto, faz parte do pacote Homebrew, assim como nos macosbrew install expect
.