Kwestia jq
potrzeby jawnego filtrowania podczas przekierowywania danych wyjściowych jest omawiana w Internecie. Ale nie jestem w stanie przekierować danych wyjściowych, jeśli jq
jest częścią łańcucha potoków, nawet jeśli używany jest filtr jawny.
Rozważać:
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
Zgodnie z oczekiwaniami dane wyjściowe w oryginalnym terminalu z jq
polecenia to:
1
3
Ale jeśli dodam jakiekolwiek przekierowanie lub potokowanie na końcu jq
polecenia, dane wyjściowe zostaną wyciszone:
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
Brak danych wyjściowych w pierwszym terminalu i out.txt jest pusty.
Próbowałem setek odmian, ale jest to zagadka nieuchwytna. Jedynym obejściem, jakie znalazłem , jak odkryłem za pomocą mosquitto_sub
The Things Network (w którym również odkryłem problem), jest zawinięcie funkcji tail i jq w skrypcie powłoki:
#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done
Następnie:
./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
I rzeczywiście, pojawia się wynik:
1
3
Jest to najnowszy jq
zainstalowany przez 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
Czy jest to (w dużej mierze nieudokumentowany) błąd w jq
lub z moim rozumieniem łańcuchów rur?
źródło
tail -f
do ciągłego wprowadzania danych do programu itee
przetwarzania danych wyjściowych. Jeśli nadal potrzebujesz odpowiedzi, sugerowałbym uproszczenie łańcucha<in.json jq '.f1' >out.json
, abyś mógł zawęzić jego przyczyny.tail -f logfile | grep 'foo bar' | awk ...
tail
bit powstał z wysiłków mających na celu rozbicie potoku (uruchomienie pierwszego polecenia, tee i przekierowanie do pliku, zakończenie tego, potok do następnego polecenia, przekierowanie do pliku itp.) I ciągłe uruchamianie go w sekcjach.<
Jest dobrym narzędziem, aby pamiętać jednak.Odpowiedzi:
Wyjście z
jq
jest buforowane, gdy przesyłane jest standardowe wyjście.Aby zażądać
jq
opróżnienia bufora wyjściowego po każdym obiekcie, użyj jego--unbuffered
opcji, npZ
jq
instrukcji:źródło
To, co tu widzisz, to buforowanie stdio w akcji. Będzie przechowywać dane wyjściowe w buforze, dopóki nie osiągnie określonego limitu (może wynosić 512 bajtów lub 4KB lub więcej), a następnie wyśle je wszystkie naraz.
Buforowanie to wyłącza się automatycznie, jeśli stdout jest podłączony do terminala, ale gdy jest podłączony do potoku (jak w twoim przypadku), włącza to zachowanie buforowania.
Zwykłym sposobem wyłączania / kontrolowania buforowania jest użycie
setvbuf()
funkcji ( więcej informacji znajduje się w tej odpowiedzi ), ale należy to zrobić w samym kodzie źródłowymjq
, więc może nie jest to dla ciebie praktyczne ...Istnieje obejście ... (włamanie, można powiedzieć.) Istnieje program o nazwie „unbuffer”, który jest dystrybuowany wraz z „expect”, który może stworzyć pseudo-terminal i połączyć go z programem. Tak więc, mimo że
jq
nadal będzie zapisywać do potoku, pomyśli, że zapisuje do terminala, a efekt buforowania zostanie wyłączony.Zainstaluj pakiet „expect”, który powinien zawierać „unbuffer”, jeśli jeszcze go nie masz ... Na przykład na Debianie (lub Ubuntu):
Następnie możesz użyć tego polecenia:
Zobacz także tę odpowiedź, aby uzyskać więcej informacji na temat „unbuffer”, możesz też znaleźć tutaj stronę podręcznika man .
źródło
jq
natywnie implementuje niebuforowane dane wyjściowe, więc nie ma potrzeby obejścia tego problemu.jq
stronę podręcznika, ale po chwili się nudziłem i zacząłem robić inne rzeczy ... Dobrze wiedzieć, że jest coś takiego! :-)stdbuf -o0
które dostarczają kod przez LD_PRELOAD i wykonująsetvbuf()
magiczne wezwanie dla ciebie. Czy to działa w systemie macOS, nie jestem pewien.expect
jest wstępnie zainstalowany na komputerach Mac,unbuffer
nie jest. Jest to jednak część pakietu Homebrew, więcbrew install expect
wystarczy na Macach .