为什么这一行无法产生输出?

为什么这一行无法产生输出?

以下内容在 bash 中不会产生任何结果:

while true ; do upower -d ; sleep 1 ; done | grep percentage | uniq

我发现这个链中最后一个甚至倒数第二个程序是什么并不重要。倒数第二个程序总是产生预期的输出,而最后一个程序总是无法产生任何结果。

( while ... done )如果我将 while 循环包装在子 shell 中(通过),或者将除最后一个命令之外的所有内容包装在子 shell 中并通过管道传输到最后一个命令,这似乎也并不重要。

我对这种行为深感困惑。

我的直觉告诉我...

...由于阻塞读取和写入而创建的链中存在某种 I/O 死锁,而且 while 循环并不总是产生输出。但是,另一方面,我以前已经做过很多次这样的事情了,没有任何问题。另外,如果是这样的话,那while循环和下一个命令之间不就存在问题了吗?所以我很困惑。如果其他人无法重现,将提供 bash 版本信息。

万一它不是立即显而易见的......

这行代码的要点是打印电池百分比的每次变化,但只打印新的电池电量。它使用轮询。我想我只需运行就能得到相同的行为

upower --monitor-detail | grep percentage | uniq

但这是一次性的事情,我并没有打算在这上面花费超过 5 秒钟的思考,直到上面的方法开始失败。这时候它就变成了一个有趣的问题。另外,我不知道监视器详细信息是否只是在幕后进行轮询(而且我没有运行 strace 来检查)。

编辑:显然,上面的--monitor-detail版本也没有产生任何东西(或者,至少看起来是这样。轮询/更新频率相当低,所以我可能没有等待足够长的时间。虽然我知道我为原始问题等待了足够长的时间)。现在我非常非常困惑。我想我应该运行那个 strace 毕竟......

答案1

您应该使用grep --line-buffered percentage,否则标准输出缓冲区将需要很长时间才能grep被其输出填充。

答案2

除了 Marco d'Itri 的答案之外,并非每个工具都支持自定义缓冲。如果您正在运行的工具没有这样做,或者您想使用自定义缓冲区大小来运行它,您可以使用stdbuf覆盖工具的缓冲行为;例如,在这种情况下,强制输出进行行缓冲:

while true ; do upower -d ; sleep 1 ; done | stdbuf -oL grep 'percentage' | uniq

强制输出不缓冲:

while true ; do upower -d ; sleep 1 ; done | stdbuf -o0 grep 'percentage' | uniq

强制输出以 512 B 为块进行缓冲:

while true ; do upower -d ; sleep 1 ; done | stdbuf -o512 grep 'percentage' | uniq

您可以添加 K、M、G、[...] 来指定以 KB、MB、GB、[...] 为单位的输出:

while true ; do upower -d ; sleep 1 ; done | stdbuf -o512K grep 'percentage' | uniq

答案3

使用任何 POSIX,sed您都可以使用 rite 命令实现行缓冲输出w

while  sleep 1
do     upower -d
done | 
sed -n /percentage/w\ /dev/fd/1 |
uniq

...将在支持/dev/fd/[num]链接的系统上工作(例如几乎任何 Linux 系统)。

但你可以这样做:

while  sleep 1
do     upower -d
done | 
sed '/percentage/!d;H;x
     /^\(.*\)\n\1$/d;g'

上面应该这样做 - 它只是将uniq和的功能打包grep到一个小sed脚本中。

相关内容