El tema de la jq
necesidad de un filtro explícito cuando la salida se redirige se discute en toda la web. Pero no puedo redirigir la salida si jq
es parte de una cadena de tuberías, incluso cuando se usa un filtro explícito.
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 se esperaba, la salida en el terminal original del jq
comando es:
1
3
Pero si agrego algún tipo de redirección o tubería al final del jq
comando, la salida se silencia:
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
No aparece ninguna salida en el primer terminal y out.txt está vacío.
He probado cientos de variaciones pero es un problema difícil de alcanzar. La única solución que he encontrado , como descubrí a través de mosquitto_sub
The Things Network (que también descubrí el problema), es envolver las funciones tail y jq en un script de shell:
#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done
Luego:
./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
Y, efectivamente, aparece la salida:
1
3
Esto es con la última jq
versión instalada a través de 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
¿Es esto un error (en gran parte indocumentado) en jq
mi comprensión de las cadenas de tuberías?
fuente
tail -f
para proporcionar una entrada continua a un programa ytee
procesar la salida. Si todavía necesitara una respuesta, le habría sugerido que simplificara la cadena para<in.json jq '.f1' >out.json
poder reducir la causa.tail -f logfile | grep 'foo bar' | awk ...
tail
bit surgió de los esfuerzos por romper la tubería (ejecute el primer comando, coloque en T y redirija al archivo, siga esa cola, canalice al siguiente comando, redirija al archivo, etc.) y ejecútelo continuamente en secciones. Sin<
embargo, es una buena herramienta para tener en cuenta.Respuestas:
La salida de
jq
se almacena cuando se canaliza su salida estándar.Para solicitar que
jq
vacíe su búfer de salida después de cada objeto, use su--unbuffered
opción, por ej.Del
jq
manual:fuente
Lo que estás viendo aquí es el buffering de C stdio en acción. Almacenará la salida en un búfer hasta que alcance un cierto límite (puede ser 512 bytes, o 4KB o más) y luego lo enviará todo de una vez.
Este almacenamiento en búfer se deshabilita automáticamente si stdout está conectado a un terminal, pero cuando está conectado a una tubería (como en su caso), habilitará este comportamiento de almacenamiento en búfer.
La forma habitual de deshabilitar / controlar el almacenamiento en búfer es usar la
setvbuf()
función (consulte esta respuesta para obtener más detalles), pero eso debería hacerse en el código fuente dejq
sí mismo, por lo que tal vez no sea algo práctico para usted ...Hay una solución alternativa ... (Un truco, se podría decir). Hay un programa llamado "unbuffer", que se distribuye con "esperar" que puede crear un pseudo-terminal y conectarlo a un programa. Por lo tanto, aunque
jq
seguirá escribiendo en una tubería, pensará que está escribiendo en una terminal y se deshabilitará el efecto de almacenamiento en búfer.Instale el paquete "expect", que debería venir con "unbuffer", si aún no lo tiene ... Por ejemplo, en Debian (o Ubuntu):
Entonces puedes usar este comando:
Consulte también esta respuesta para obtener más detalles sobre "unbuffer", y también puede encontrar una página de manual aquí .
fuente
jq
implementa de forma nativa la salida sin búfer, por lo que no hay necesidad de la solución.jq
página de manual, pero me aburrí después de un tiempo y fui a hacer otras cosas ... ¡Es bueno saber que hay algo así! :-)stdbuf -o0
que inyectarán código a través de LD_PRELOAD y harán lasetvbuf()
llamada mágica por usted. Si funciona en macOS, no estoy seguro.expect
está preinstalado en macos,unbuffer
no lo está. Sin embargo, es parte del paquete Homebrew, por lo que en macosbrew install expect
lo hará.