Sử dụng jq trong chuỗi ống không tạo ra đầu ra


12

Vấn đề jqcần một bộ lọc rõ ràng khi đầu ra được chuyển hướng được thảo luận trên tất cả các trang web. Nhưng tôi không thể chuyển hướng đầu ra nếu jqlà một phần của chuỗi ống, ngay cả khi sử dụng bộ lọc rõ ràng.

Xem xét:

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

Như mong đợi, đầu ra trong thiết bị đầu cuối ban đầu từ jqlệnh là:

1
3

Nhưng nếu tôi thêm bất kỳ loại chuyển hướng hoặc đường ống nào vào cuối jqlệnh, đầu ra sẽ im lặng:

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

Không có đầu ra xuất hiện trong thiết bị đầu cuối đầu tiên và out.txt trống.

Tôi đã thử hàng trăm biến thể nhưng đó là một vấn đề khó nắm bắt. Cách giải quyết duy nhất mà tôi đã tìm thấy , như được phát hiện thông qua mosquitto_subvà Mạng điều (nơi tôi cũng đã phát hiện ra vấn đề), là bọc các hàm đuôi hàm jq trong tập lệnh shell:

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

Sau đó:

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

Và chắc chắn, đầu ra xuất hiện:

1
3

Đây là bản jqcài đặt mới nhất qua 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

Đây có phải là một lỗi (phần lớn không có giấy tờ) trong jqhoặc với sự hiểu biết của tôi về chuỗi ống?


1
FWIW bạn có một thiết lập khá (tốt, hơi) ở đây, sử dụng tail -fđể cung cấp đầu vào liên tục cho một chương trình và teeđể xử lý đầu ra. Nếu bạn vẫn cần câu trả lời, tôi đã đề nghị đơn giản hóa chuỗi để <in.json jq '.f1' >out.jsonbạn có thể thu hẹp những gì gây ra nó.
David Z

Xem thêm BashFAQ # 9 - Bộ đệm là gì? Hoặc, tại sao dòng lệnh của tôi không tạo ra đầu ra:tail -f logfile | grep 'foo bar' | awk ...
Charles Duffy

Tất cả lời khuyên tuyệt vời cho những nỗ lực trong tương lai, cảm ơn bạn. FWIW, tailbit đến từ những nỗ lực phá vỡ đường ống (chạy lệnh đầu tiên, tee và chuyển hướng đến tệp, nối đuôi, chuyển sang lệnh tiếp theo, chuyển hướng đến tệp, v.v.) và chạy liên tục trong các phần. Đây <là một công cụ tốt để ghi nhớ mặc dù.
Heath Raftery

Câu trả lời:


19

Đầu ra từ jqđược đệm khi đầu ra tiêu chuẩn của nó được dẫn.

Để yêu cầu jqxóa bộ đệm đầu ra của nó sau mỗi đối tượng, hãy sử dụng --unbufferedtùy chọn của nó , ví dụ:

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

Từ jqhướng dẫn:

--unbuffered

Xóa đầu ra sau khi mỗi đối tượng JSON được in (hữu ích nếu bạn dẫn một nguồn dữ liệu chậm vào jqjqđầu ra của đường ống ở nơi khác).


Hơn nữa, cách tôi sẽ gỡ lỗi này, để tìm ra rằng bộ đệm đầu ra là vấn đề, giả sử tôi sẽ không đơn giản đoán rằng, sẽ chạy phần 'jq' dưới 'ltrace' và / hoặc 'strace'. Rõ ràng là nó đang gọi các hàm đầu ra C stdio, nhưng không gọi là tòa nhà viết (2).
AnotherSmellyGeek

1
@AntherSmellyGeek Có thể hoặc tiện ích theo dõi tương đương trên Unices của chúng tôi (lưu ý rằng OP đang sử dụng Homebrew, có nghĩa là chúng trên macOS và tôi trên OpenBSD, không có công cụ Linux nào). Một khả năng khác là chỉ cần biết rằng bộ đệm đầu ra có thể xảy ra trong một số trường hợp nhất định :-)
Kusalananda

Xuất sắc. Và thực sự đánh giá cao tất cả các lời khuyên về gỡ lỗi này trong tương lai. Bộ đệm là một trong những nghi ngờ đầu tiên của tôi, nhưng hành vi khác nhau đối với đường ống đang làm thất vọng những nỗ lực sửa lỗi của tôi.
Heath Raftery

6

Những gì bạn đang thấy ở đây là bộ đệm C stdio đang hoạt động. Nó sẽ lưu trữ đầu ra trên một bộ đệm cho đến khi đạt đến một giới hạn nhất định (có thể là 512 byte hoặc 4KB hoặc lớn hơn) và sau đó gửi tất cả cùng một lúc.

Bộ đệm này tự động bị vô hiệu hóa nếu thiết bị xuất chuẩn được kết nối với thiết bị đầu cuối, nhưng khi nó được kết nối với một đường ống (chẳng hạn như trong trường hợp của bạn), nó sẽ kích hoạt hành vi đệm này.

Cách thông thường để vô hiệu hóa / kiểm soát bộ đệm là sử dụng setvbuf()chức năng (xem câu trả lời này để biết thêm chi tiết), nhưng điều đó cần phải được thực hiện trong mã nguồn của jqchính nó, vì vậy có lẽ không phải là điều gì thực tế đối với bạn ...

Có một cách giải quyết ... (Một hack, người ta có thể nói.) Có một chương trình gọi là "unbuffer", được phân phối với "mong đợi" có thể tạo ra một thiết bị đầu cuối giả và kết nối nó với một chương trình. Vì vậy, mặc dù jqvẫn sẽ ghi vào một đường ống, nó sẽ nghĩ rằng nó đang ghi vào một thiết bị đầu cuối và hiệu ứng đệm sẽ bị vô hiệu hóa.

Cài đặt gói "mong đợi", đi kèm với "unbuffer", nếu bạn chưa có nó ... Ví dụ, trên Debian (hoặc Ubuntu):

$ sudo apt-get install expect

Sau đó, bạn có thể sử dụng lệnh này:

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

Xem thêm câu trả lời này để biết thêm chi tiết về "unbuffer" và bạn cũng có thể tìm thấy một trang nam ở đây .


Tôi thích điều đó bạn đã giải thích tại sao hành vi quan sát được xảy ra, nhưng như Kusalananda đã chỉ ra, jqthực chất là thực hiện đầu ra không có bộ đệm nên không cần phải giải quyết.
David Z

Ah rất đẹp! Tôi bắt đầu tìm kiếm trong jqtrang người đàn ông nhưng sau đó chán nản và đi làm việc khác ... Thật tốt khi biết có điều gì đó như thế! :-)
filbranden

1
Protip, GNU coreutils đi kèm stdbuf -o0sẽ tiêm mã thông qua LD_PRELOAD và thực hiện setvbuf()cuộc gọi ma thuật cho bạn. Cho dù nó hoạt động trên macOS, tôi không chắc chắn.
dùng1686

1
Trong khi expectđược cài đặt sẵn trên macos thì unbufferkhông. Tuy nhiên, nó là một phần của gói Homebrew, vì vậy trên macos, brew install expectsẽ làm được.
Heath Raftery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.