jq
出力をリダイレクトする際の明示的なフィルタの必要性について、Web全体で議論がありました。ただし、jq
パイプラインチェーンの一部である場合は、明示的なフィルタを使用しても出力をリダイレクトできません。
考慮する:
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
予想通り、コマンドの生端末出力は次jq
のようになります。
1
3
ただし、コマンドの最後にリダイレクトまたはパイプを追加すると、jq
出力は自動的に保持されます。
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
最初の端末には出力が表示されず、out.txtは空です。
何百ものバリエーションを試しましたが、これは理解しにくい問題です。ただ私が見つけた解決策、mosquitto_sub
IoTを通じて発見したとおり(ここで問題を発見しました)尾を包むことです。そしてシェルスクリプトのjq関数:
#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done
それから:
./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
もちろん、結果は次のようになります。
1
3
これはjq
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
jq
これはパイプラインチェーンを理解する際の(ほとんど文書化されていない)エラーですか?
答え1
jq
標準出力が端末でない場合、その出力はバッファリングされます。
jq
各オブジェクトの出力バッファを後でフラッシュするように要求するには、--unbuffered
そのオプションを使用します。
tail -f in.txt | jq --unbuffered '.f1' | tee out.txt
jq
マニュアルから:
--unbuffered
各JSONオブジェクトを印刷してから出力をフラッシュします(遅いデータソースを別の場所にパイプして出力を
jq
パイプする場合にjq
便利です)。
答え2
ここで見ることができるのは、C stdioバッファリングが機能する様子です。特定の制限(512バイト、4KB以上)に達するまで、出力をバッファに保存してから、すべての出力を一度に送信します。
stdoutが端末に接続されると、このバッファリングは自動的に無効になりますが、パイプに接続すると(あなたの場合のように)、このバッファリング動作が有効になります。
バッファリングを無効化/制御する一般的な方法は、このsetvbuf()
機能を使用することです(参照:この回答詳細については)しかし、これはjq
ソースコード自体で行わなければならないので、実用的ではないかもしれません。
回避策があります...(誰かがハッキングと言うことができます)、「expect」と一緒に配布される「unbuffer」というプログラムがあります。したがって、jq
パイプに書き込みを続けていても、端末に書き込んでいると考えてバッファリング効果を無効にします。
まだない場合は、「unbuffer」に付属の「expect」パッケージをインストールします。たとえば、Debian(またはUbuntu)の場合:
$ sudo apt-get install expect
その後、次のコマンドを使用できます。
$ tail -f in.txt | unbuffer -p jq '.f1' | tee out.txt
また、見ることができますこの回答「unbuffer」の詳細を見つけることができます。マニュアルページもここにあります。