我正在编写一个 shell 包装脚本,它应该充当寻呼机(在 stdin 上接收输入并在 tty 上执行输出)。
该包装器准备环境和命令行,然后在管道中启动两个进程:第一个是非交互式过滤器,最后一个是需要控制 tty 的实际寻呼机(或者,它生成实际的寻呼机本身):
#!/bin/bash
...
col -bx | bat "${bat_args[@]}" --paging=always
最终的进程树如下:
<parent>
\- wrapper.sh
|- col -bx (dies)
\- bat ...
\- less ...
该col -bx
进程在过滤输入后退出,并由 shell 收获。
是否有可能摆脱 shell 进程,以使其在寻呼机运行时不会一直处于挂起状态?
我想到了一个使用进程替换的解决方法:
exec bat "${bat_args[@]}" --paging=always < <(col -bx)
但是,col -bx
进程不会被 shell 回收,而是保持 Z 状态。有没有“正确”的方式来编写这个包装器?
您还可以执行以下操作:
或者:
(不适用于 pdksh 派生的 ksh 实现)
最后,功能上等同于您使用进程替换进行重定向所做的事情,但跳过了额外的 fd 改组和命名管道创建(或在
/dev/fd/x
具有它们的系统上打开(大多数))。无论如何,要收割一个进程,您需要等待它。使用
ksh
/zsh
/bash -O lastpipe
和上述方法,col
将成为最终执行的进程的子进程bat
,因此在死亡bat
时将获得 SIGCHLDcol
,它如何处理它取决于它。如果它不处理该 SIGCHLD(很少有应用程序会处理它们自己没有生成的进程),col
它将显示为僵尸进程直到bat
终止,此后该col
进程将重新成为子子收割者的父进程或init
并在那里进行处理。无论如何,请注意,除非您设置选项,否则
A | B
ksh 不会等待。A
pipefail
现在,根据 XSI 选项下的 POSIX,这也适用于 Linux,
(重点是我的)。
现在,并不是所有的 shell 都允许您忽略 SIGCHLD,因为它对于其功能至关重要(shell 的全部目的是运行命令并报告其退出状态),但看起来当前版本
bash
(我测试的是 5.2.21)至少可以这样做,因此:col
只要在退出bat
之前不取消忽略 SIGCHLD,正在运行的进程就永远不会变成僵尸。col
无论如何,僵尸不一定是坏事。僵尸阶段是进程生命的一部分,大多数进程在死亡和其父进程(或
init
子进程)确认死亡之间都会经历这个阶段。