Boru zincirinde jq kullanmak çıktı üretmez


12

jqÇıktı yeniden yönlendirildiğinde açık bir filtreye ihtiyaç duyulması sorunu tüm web üzerinde tartışılmaktadır. Ancak jq, açık bir filtre kullanılıyor olsa bile, bir boru zincirinin parçasıysa çıktıyı yeniden yönlendiremiyorum .

Düşünmek:

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

Beklendiği gibi, orijinal terminaldeki jqkomuttan çıkış :

1
3

Ancak, jqkomutun sonuna herhangi bir yönlendirme veya borulama eklersem , çıktı sessiz kalır :

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

İlk uçbirimde çıkış görünmüyor ve out.txt boş.

Yüzlerce varyasyonu denedim ama bu zor bir konu. Bulduğum gibi bulduğum tek geçici çözümmosquitto_sub ve (bu da sorunu keşfettiğim şey) The Things Network, kuyruk ve jq işlevlerini bir kabuk komut dosyasına sarmaktır:

#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done

Sonra:

./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

Ve yeterince emin olun, çıktı görünür:

1
3

jqHomebrew üzerinden en son yüklenen budur:

$ echo $SHELL
/bin/bash
$ jq --version
jq-1.5
$ brew install jq
Warning: jq 1.5_3 is already installed and up-to-date

Bu jq, boru zincirlerini anladığımda veya benim bildiğimde (büyük ölçüde belgelenmemiş) bir hata mı?


1
FWIW burada tail -fbir programa sürekli girdi sağlamak teeve çıktıyı işlemek için oldukça (iyi, biraz) garip bir kurulumunuz var . Hâlâ bir cevaba ihtiyacınız olsaydı, <in.json jq '.f1' >out.jsonneyin sebep olduğunu daraltabilmeniz için zinciri basitleştirmeyi önerirdim .
David Z

Ayrıca bakınız BashFAQ # 9 - Arabelleğe alma nedir? Veya, komut tail -f logfile | grep 'foo bar' | awk ...
Charles Duffy

Gelecekteki çabalarınız için tüm büyük tavsiyeler, teşekkür ederim. FWIW, tailbit, boruyu parçalama çabalarından kaynaklandı (ilk komutu çalıştır, dosyaya yönlendir ve yönlendir, kuyruk, sonraki komuta boru, dosyaya yönlendir, vb.) Ve bölümlerde sürekli olarak çalıştır. <Gerçi akılda tutmak iyi bir araçtır.
Heath Raftery

Yanıtlar:


20

jqStandart çıkışı borulandığında gelen çıkış arabelleğe alınır.

jqHer nesneden sonra çıktı arabelleğini temizlemesini istemek için --unbufferedseçeneğini kullanın, örn.

tail -f in.txt | jq --unbuffered '.f1' | tee out.txt

Gönderen jqmanuel:

--unbuffered

Her JSON nesnesi yazdırıldıktan sonra çıktıyı temizleyin (yavaş bir veri kaynağını başka bir yere jqboru hattına bağlıyorsanız ve jqbaşka bir yere boru hattını kullanıyorsanız faydalıdır ).


Ayrıca, çıktı ayıklamanın sadece ljk ve / veya 'strace' altında 'jq' kısmını çalıştırmak olacağını varsayarak, çıktı tamponlama sorunu olduğunu anlamak için bu hata ayıklama yolu. C stdio çıkış işlevlerini çağırdığı, ancak write (2) sistem çağrısını çağırmadıkları açıktır.
AnotherSmellyGeek

1
@AnotherSmellyGeek Muhtemelen, ya da Unices'umuzdaki eşdeğer izleme yardımcı programı (OP'nin Homebrew kullandığını, yani macOS'ta olduklarını ve her ikisinde de bu Linux araçlarına sahip olduğum anlamına gelmediğini unutmayın). Başka bir olasılık sadece çıktı tamponlama belirli koşullar altında olabileceğini bilmek :-)
Kusalananda

Parlak. Ve gelecekte bu hata ayıklama ile ilgili tüm tavsiyeleri gerçekten takdir ediyorum. Arabelleğe alma ilk şüphelerimden biriydi, ancak boru tesisatı için farklı davranış, hata ayıklama çabalarımı yumuşatmaktı.
Heath Raftery

6

Burada gördüğünüz şey, hareket halinde C stdio tamponlama. Belirli bir sınıra ulaşana kadar (512 bayt veya 4KB veya daha büyük olabilir) çıktıyı bir arabellekte saklar ve sonra hepsini bir kerede gönderir.

Stdout bir terminale bağlıysa bu tamponlama otomatik olarak devre dışı kalır, ancak bir boruya (sizin durumunuzda olduğu gibi) bağlandığında, bu tamponlama davranışını etkinleştirir.

Arabelleğe almayı devre dışı bırakmanın / kontrol etmenin genel yolu, setvbuf()işlevi kullanmaktır ( daha fazla ayrıntı için bu cevaba bakın), ancak bunun jqkendi kaynak kodunda yapılması gerekir, bu yüzden sizin için pratik bir şey olmayabilir ...

Bir geçici çözüm var ... (Saldırı, biri diyebilir.) "Arabelleğe alma" adlı bir program var, bu "sözde" ile dağıtılmış ve sahte bir terminal oluşturabilir ve bunu bir programa bağlayabilir. Bu nedenle, jqbir boruya yazılsa bile, bir terminale yazdığını düşünecek ve tamponlama etkisi devre dışı bırakılacak.

Henüz sahip değilseniz, "arabelleğe alma" ile birlikte gelmesi gereken "beklemek" paketini yükleyin ... Örneğin, Debian (veya Ubuntu) üzerinde:

$ sudo apt-get install expect

Sonra bu komutu kullanabilirsiniz:

$ tail -f in.txt | unbuffer -p jq '.f1' | tee out.txt

Ayrıca "arabelleğe alma" hakkında daha fazla ayrıntı için bu yanıta bakın . Burada da bir kılavuz sayfası bulabilirsiniz .


Gözlemlenen davranışın neden oluştuğunu açıkladığınızı sevdim, ancak Kusalananda'nın işaret ettiği gibi, jqdoğal olarak arabelleksiz çıktı uygular , böylece geçici çözüme gerek yoktur.
David Z

Ah çok hoş! jqAdam sayfasına bakmaya başladım ama bir süre sonra sıkıldım ve başka şeyler yapmaya gittim ... Böyle bir şey olduğunu bilmek güzel! :-)
filbranden

1
Protip, GNU coreutils ile birlikte gelir ve stdbuf -o0LD_PRELOAD yoluyla kod enjekte eder ve setvbuf()sizin için sihirli çağrı yapar. MacOS üzerinde çalışıyor olsun, emin değilim.
user1686

1
expectMacos'a önceden kurulmuş olsa da unbufferdeğildir. Ancak Homebrew paketinin bir parçası, bu yüzden macos, brew install expectyapacak.
Heath Raftery
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.