出力がリダイレクトされるときにjq
が明示的なフィルターを必要とする問題は、ウェブ全体で議論されています。しかし、明示的なフィルターが使用されている場合でも、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
およびThe Things Network(これも私が問題を発見した場所)を通じて発見されたように、尾を折り返すandシェルスクリプトの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
これは、Homebrewを介してインストールされた最新のjq
を使用しています。
$ 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
の(ほとんど文書化されていない)バグですか、それともパイプチェーンについての私の理解によるものですか?
jq
からの出力は、標準出力がパイプされるときにバッファーに入れられます。
すべてのオブジェクトの後にjq
が出力バッファをフラッシュすることを要求するには、その--unbuffered
オプションを使用します。
tail -f in.txt | jq --unbuffered '.f1' | tee out.txt
jq
マニュアルから:
--unbuffered
各JSONオブジェクトが出力された後、出力をフラッシュします(遅いデータソースを
jq
にパイプし、jq
の出力を別の場所にパイプする場合に便利です)。
ここに表示されているのは、動作中のC stdioバッファリングです。一定の制限(512バイト、または4KB以上の可能性があります)に達するまで出力をバッファーに格納し、それを一度に送信します。
Stdoutがターミナルに接続されている場合、このバッファリングは自動的に無効になりますが、(ケースのように)パイプに接続されている場合、このバッファリング動作が有効になります。
バッファリングを無効化/制御する通常の方法は、setvbuf()
関数を使用することです(詳細については この回答 を参照)。ただし、これはjq
自体なので、おそらく実用的ではありません...
回避策があります...(ハック、誰かが言うかもしれません。)疑似端末を作成し、それをプログラムに接続できる「expect」とともに配布される「unbuffer」と呼ばれるプログラムがあります。したがって、jq
がまだパイプに書き込みを行っている場合でも、ターミナルへの書き込みであると見なされ、バッファリング効果は無効になります。
「expect」パッケージをインストールします。まだ持っていない場合は、「unbuffer」が付属しているはずです...たとえば、Debian(またはUbuntu)の場合:
$ Sudo apt-get install expect
その後、次のコマンドを使用できます。
$ tail -f in.txt | unbuffer -p jq '.f1' | tee out.txt
「unbuffer」の詳細については this answer も参照してください。 manページもここにあります を見つけることができます。