假设我在 shell 脚本中有类似的东西
command1 | command2
现在我怎样才能让它在退出command2
时终止command1
?
编辑:
command2
当尝试从标准输入读取并注意到它返回 0 时,默认情况下通常会发生这种情况。您有一些具体的示例吗?
干得好:echo test | xmobar
xmobar
我想这也可以用作替代品
#!/bin/sh
while :; do :; done
答案1
这条评论解释当管道的另一端关闭时程序如何终止(或不终止):
关闭管道根本
command1
不会说明command2
任何问题。在command2
读取管道、清空它并获得 EOF之前不会发生任何事情。即使如此,如果仍有工作要做,它也不必退出。类似地,如果command2
先退出,command1
则在写入管道之前不会发现它,此时它会收到SIGPIPE
.如果它捕获了这一点,它也可以继续进行其他工作。
所以这就是可以自动发生的事情。现在你的问题是:
command1 | command2
我怎样才能使它在退出
command2
时终止command1
[即使command2
没有注意到或不想终止]?
如果您的 shell 在单独的进程组中运行管道,请尝试终止整个组。例如,当启用作业控制时,Bash 会执行此操作 ( set -m
)。默认情况下,它对于交互式 shell(在支持它的系统上)启用,但在脚本中不启用。
示例代码:
#!/bin/bash
set -m
( command1; kill -s INT 0 ) | command2
整个管道在一个单独的进程组中运行,其 PGID(进程组 ID)等于第一部分的 PID,在本例中是一个子 shell。kill 0
向其自己的进程组发送信号。这样我们就command2
在command1
终止后发出信号。
笔记:
- 在 Bash 中
kill
是内置的,因此kill
在示例中不是分配了 PID 和 PGID 的单独进程。因此上面这句话“自己的进程组”并不严格,应该是“子shell的进程组”。它仍然是我们想要使用的确切进程组。内置kill
的或/bin/kill
可以使用的,任何一个都可以。 command2
可能会改变自己的进程组。如果是这样,该解决方案将不起作用。- 因为几乎所有信号都
command2
可能忽略该信号或以几乎任何方式处理它。选择适合您需求的信号。 kill
终止后立即调用command1
可能为时过早。它可能会导致在读取和处理;command2
生成的所有数据之前退出。command1
您可能希望它处理所有数据。根据command2
行为方式,延迟可能会有用kill
(例如sleep 2; kill …
)。请注意,(通用)Linux 不是实时操作系统,因此,如果情况足够糟糕,任何固定的延迟都可能会变得太短。