La question d' jq
avoir besoin d'un filtre explicite lorsque la sortie est redirigée est discutée partout sur le Web. Mais je ne peux pas rediriger la sortie si elle jq
fait partie d'une chaîne de canalisations, même lorsqu'un filtre explicite est utilisé.
Considérer:
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
Comme prévu, la sortie dans le terminal d'origine de la jq
commande est:
1
3
Mais si j'ajoute une sorte de redirection ou de tuyauterie à la fin de la jq
commande, la sortie devient silencieuse:
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
Aucune sortie n'apparaît dans le premier terminal et out.txt est vide.
J'ai essayé des centaines de variantes mais c'est un problème difficile à cerner. La seule solution de contournement que j'ai trouvée , telle que découverte par le biais de mosquitto_sub
The Things Network (où j'ai également découvert le problème), est d'envelopper les fonctions tail et jq dans un script shell:
#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done
Alors:
./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
Et bien sûr, la sortie apparaît:
1
3
C'est avec la dernière version jq
installée 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
Est-ce un bug (largement non documenté) dans jq
ou avec ma compréhension des chaînes de tuyaux?
la source
tail -f
pour fournir une entrée continue à un programme ettee
pour traiter la sortie. Si vous aviez encore besoin d'une réponse, j'aurais suggéré de simplifier la chaîne pour<in.json jq '.f1' >out.json
que vous puissiez affiner la cause.tail -f logfile | grep 'foo bar' | awk ...
tail
bit est né des efforts pour casser le tuyau (exécutez la première commande, tee et rediriger vers le fichier, queue que, tuyau vers la commande suivante, rediriger vers le fichier, etc.) et l'exécuter en continu dans les sections. C'est<
un bon outil à garder à l'esprit.Réponses:
La sortie de
jq
est mise en mémoire tampon lorsque sa sortie standard est canalisée.Pour demander de
jq
vider son tampon de sortie après chaque objet, utilisez son--unbuffered
option, par exempleDu
jq
manuel:la source
Ce que vous voyez ici, c'est la mise en mémoire tampon C stdio en action. Il stockera la sortie sur un tampon jusqu'à ce qu'il atteigne une certaine limite (peut-être 512 octets, ou 4 Ko ou plus), puis enverra tout cela en même temps.
Cette mise en mémoire tampon est automatiquement désactivée si stdout est connecté à un terminal, mais lorsqu'il est connecté à un tuyau (comme dans votre cas), il active ce comportement de mise en mémoire tampon.
La façon habituelle de désactiver / contrôler la mise en mémoire tampon est d'utiliser la
setvbuf()
fonction (voir cette réponse pour plus de détails), mais cela devrait être fait dans le code sourcejq
lui-même, donc peut-être pas quelque chose de pratique pour vous ...Il y a une solution de contournement ... (Un hack, pourrait-on dire.) Il existe un programme appelé "unbuffer", qui est distribué avec "expect" qui peut créer un pseudo-terminal et le connecter à un programme. Ainsi, même s'il
jq
écrit toujours sur un canal, il pensera qu'il écrit sur un terminal et l'effet de mise en mémoire tampon sera désactivé.Installez le paquet "expect", qui devrait venir avec "unbuffer", si vous ne l'avez pas déjà ... Par exemple, sur Debian (ou Ubuntu):
Ensuite, vous pouvez utiliser cette commande:
Voir aussi cette réponse pour plus de détails sur "unbuffer", et vous pouvez trouver une page de manuel ici aussi .
la source
jq
implémente nativement une sortie non tamponnée, il n'y a donc pas besoin de contourner ce problème.jq
page de manuel mais je me suis ennuyé après un moment et je suis allé faire d'autres choses ... Bon de savoir qu'il y a quelque chose comme ça! :-)stdbuf -o0
qui injecteront du code via LD_PRELOAD et feront l'setvbuf()
appel magique pour vous. Que cela fonctionne sur macOS, je ne suis pas sûr.expect
soit préinstallé sur macOS, ceunbuffer
n'est pas le cas. Cependant, il fait partie du package Homebrew, donc sur macos,brew install expect
fera l'affaire.