当输出被重定向时需要一个显式过滤器的问题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 发现的(我也发现了这个问题),是将 tail和jq 函数包装在 shell 脚本中:
#!/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
我对管道链的理解中的(大部分未记录的)错误吗?
jq
当其标准输出通过管道传输时,来自的输出被缓冲。要请求
jq
在每个对象之后刷新其输出缓冲区,请使用其--unbuffered
选项,例如从
jq
手册:您在这里看到的是 C stdio 缓冲在起作用。它将输出存储在缓冲区中,直到达到某个限制(可能是 512 字节,或 4KB 或更大),然后一次性发送所有内容。
如果 stdout 连接到终端,则此缓冲会自动禁用,但是当它连接到管道时(例如在您的情况下),它将启用此缓冲行为。
禁用/控制缓冲的常用方法是使用该
setvbuf()
函数(有关更多详细信息,请参见此答案),但这需要在其jq
自身的源代码中完成,因此对您来说可能不实用......有一种解决方法......(有人可能会说是黑客。)有一个名为“unbuffer”的程序,它与“expect”一起分发,可以创建一个伪终端并将其连接到一个程序。因此,即使
jq
仍将写入管道,它也会认为它正在写入终端,并且缓冲效果将被禁用。安装“expect”包,如果你还没有它,它应该带有“unbuffer”......例如,在 Debian(或 Ubuntu)上:
然后你可以使用这个命令:
有关“unbuffer”的更多详细信息,另请参阅此答案,您也可以在此处找到手册页。