Das Problem, jq
dass ein expliziter Filter erforderlich ist, wenn die Ausgabe umgeleitet wird, wird im gesamten Web behandelt. Ich kann die Ausgabe jedoch nicht umleiten, wenn sie jq
Teil einer Pipe-Kette ist, selbst wenn ein expliziter Filter verwendet wird.
Erwägen:
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
Wie erwartet jq
lautet die Ausgabe des Befehls im ursprünglichen Terminal :
1
3
Wenn ich am Ende des jq
Befehls eine Umleitung oder ein Piping hinzufüge , wird die Ausgabe stumm geschaltet:
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
Im ersten Terminal wird keine Ausgabe angezeigt und out.txt ist leer.
Ich habe Hunderte von Variationen ausprobiert, aber es ist ein schwer fassbares Problem. Die einzige Problemumgehung habe ich festgestellt , wie entdeckt durch mosquitto_sub
und die Dinge Network (das war , wo ich auch das Problem entdeckt), ist die Schwanz zu wickeln und jq Funktionen in einem Shell - Skript:
#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done
Dann:
./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
Und sicher erscheint die Ausgabe:
1
3
Dies ist mit der neuesten jq
Version von Homebrew installiert:
$ echo $SHELL
/bin/bash
$ jq --version
jq-1.5
$ brew install jq
Warning: jq 1.5_3 is already installed and up-to-date
Ist dies ein (größtenteils undokumentierter) Fehler in jq
oder mit meinem Verständnis von Rohrketten?
quelle
tail -f
, um einem Programm fortlaufende Eingaben zu geben undtee
die Ausgabe zu verarbeiten. Wenn Sie noch eine Antwort benötigen, hätte ich vorgeschlagen, die Kette zu vereinfachen,<in.json jq '.f1' >out.json
damit Sie eingrenzen können, was die Ursache ist.tail -f logfile | grep 'foo bar' | awk ...
tail
Bit ist aus dem Versuch entstanden, die Pipe abzubrechen (den ersten Befehl ausführen, tee und in die Datei umleiten, tailen, zum nächsten Befehl umleiten, in die Datei umleiten usw.) und kontinuierlich in Abschnitten auszuführen. Dies<
ist jedoch ein gutes Werkzeug, das Sie im Hinterkopf behalten sollten.Antworten:
Die Ausgabe von
jq
wird gepuffert, wenn die Standardausgabe weitergeleitet wird.Um anzufordern, dass
jq
der Ausgabepuffer nach jedem Objekt geleert wird, verwenden Sie seine--unbuffered
Option, zAus dem
jq
Handbuch:quelle
Was Sie hier sehen, ist die C stdio-Pufferung in Aktion. Die Ausgabe wird in einem Puffer gespeichert, bis eine bestimmte Grenze erreicht ist (möglicherweise 512 Byte oder 4 KB oder mehr), und dann auf einmal gesendet.
Diese Pufferung wird automatisch deaktiviert, wenn stdout mit einem Terminal verbunden ist. Wenn es jedoch mit einer Pipe verbunden ist (wie in Ihrem Fall), wird dieses Pufferverhalten aktiviert.
Die übliche Methode zum Deaktivieren / Steuern der Pufferung ist die Verwendung von
setvbuf()
Funktion (siehe diese Antwort für weitere Einzelheiten). Dies müsste jedoch im Quellcode vonjq
selbst erfolgen, sodass es für Sie möglicherweise nicht praktikabel ist ...Es gibt eine Problemumgehung ... (Ein Hack, könnte man sagen.) Es gibt ein Programm namens "Unbuffer", das mit "Expect" verteilt wird und ein Pseudoterminal erstellen und dieses mit einem Programm verbinden kann. Obwohl
jq
weiterhin in eine Pipe geschrieben wird, wird davon ausgegangen, dass in ein Terminal geschrieben wird, und der Puffereffekt wird deaktiviert.Installieren Sie das Paket "expect", das mit "unbuffer" geliefert werden sollte, falls Sie es noch nicht haben ... Zum Beispiel unter Debian (oder Ubuntu):
Dann können Sie diesen Befehl verwenden:
Weitere Informationen zu "Unbuffer" finden Sie auch in dieser Antwort. Hier finden Sie auch eine Manpage .
quelle
jq
implementiert es die ungepufferte Ausgabe nativ, sodass die Problemumgehung nicht erforderlich ist.jq
Manpage nachzuschlagen, aber nach einer Weile langweilte ich mich und machte andere Sachen ... Gut zu wissen, dass es so etwas gibt! :-)stdbuf -o0
die Code über LD_PRELOAD einschleusen und densetvbuf()
magischen Aufruf für Sie erledigen . Ob es unter macOS funktioniert, weiß ich nicht.expect
auf macos vorinstalliert,unbuffer
ist es nicht. Es ist jedoch Teil des Homebrew-Pakets, so dass es auf Macos ausreichtbrew install expect
.