从“超时”到“wc -l”的管道输出失败

时间:2016-01-29 16:06:56

标签: linux console timeout pipe signals

我正在尝试计算给定时间段内的所有击键次数。 目前我的代码如下所示:

Killed

返回

$ timeout -s 9 20s xinput test 8
key release 36 
key press   42 
key release 42 
key press   26 
key release 26 
key press   28 
key release 28 
key press   38 
key release 38 
key press   46 
key release 46 
key press   31 
key release 31 
key press   41 
key release 41 
key press   26 
key release 26 
Killed

而不是预期的按键次数。 当我在没有管道的情况下运行超时时输出是预期的

{{1}}

既不更改信号也不添加参数--preserve-status修复此问题。

我想知道为什么这不起作用。 “超时”是否以某种方式劫持xinputs stdout?

修改 但是添加--foreground解决了我的问题。但我不明白为什么。

2 个答案:

答案 0 :(得分:0)

它的工作原理是因为这样,timeout命令不会杀死任何子进程,从而避免让wc对行进行计数。

$ timeout --foreground -s 15 10s xinput test 8 | wc -l

另一种方式是这样的:

$ xinput test 8 > output.log & sleep 10 && wc -l output.log && kill $!

那样,你不需要超时。第一部分将xinput输出重定向到文件并将命令发送到后台然后休眠(等待文件接收到一些数据),睡眠结束后我们运行wc并优雅地杀死xinput后台命令。

答案 1 :(得分:0)

使用--foreground时,wc -l只会看到EOF,因此会打印行数。

如果没有--foregroundwc -l会被SIGKILL杀死。 (我用另一个终端的strace -p $(pidof wc)跟踪它。)

跟踪timeout本身,看看它与--foreground有什么不同:

除了内存地址不同之外:

时间过后,--foreground只会为分叉的孩子(wait(2))杀死clone(2),然后退出正常的exit_group(2)系统调用。

没有--foreground,它确实

setpgid(0, 0)                           = 0
分给孩子之前

在时间到期后,它kill()是孩子,然后用kill(0, SIGKILL)杀死自己。

来自kill(2)

  

如果pid等于0,则sig被发送到调用进程的进程组中的每个进程。

我忘记了信号语义的细节,我不确定为什么这解释了wc收到SIGKILL。根据{{​​1}},pstreewc都是bash的孩子,而不是彼此的孩子。但是,流程按它们在管道中出现的顺序启动。

xinput手册页说明了timeout(1):“在此模式下,COMMAND的子项不会超时”。似乎这包括管道中的后续元素。 IDK如果shell可以安排管道的不同元素不在同一个进程组中以避免这种情况,或者是什么。