有些程序在STDIN
关闭时会退出。那些通过“端口”在 Erlang/Elixir 监督下很好地完成这项工作的人。
对于那些不这样做的人,Elixir 文档建议这个包装脚本:
#!/bin/bash
# wrapper.sh
"$@" &
pid=$!
while read line ; do
:
done
kill -KILL $pid
这允许调用wrapper.sh my_script arg1 arg2
.wrapper.sh
启动并后台指定的程序,当wrapper.sh
sSTDIN
关闭时,其阻塞read
结束并终止后台进程。
然而,这种方法有一个缺点:如果后台进程因其他原因终止,wrapper.sh
则不会注意到,因此 Erlang/Elixir 也没有。
我想修改此脚本以执行以下操作:
在循环中:
- 如果
STDIN
关闭,则杀死$pid
- 如果
$pid
死了,退出 - 否则,短暂睡眠
谁能建议一种方法来做到这一点?
答案1
你可以$pid
像这样检查是否已经死了:
while kill -0 $pid >/dev/null; do
# $pid is still alive
done
# $pid is dead or you lack permissions to send signals to it
答案2
如果您的 shell 是dash
/busybox
或ksh93
,zsh
您可以在 上设置陷阱SIGCHLD
:
#! /bin/sh
trap exit CHLD
"$@" &
while read line; do :; done
kill $!
wait
一旦"$@" &
进程退出或read
获得EOF
.
但bash
有一个令人讨厌的read
内置功能,那就是重新启动当被信号中断时就位,所以需要一些更笨重的东西。您可以安排后台进程在退出时向其父进程发送终止信号:
#! /bin/sh
{ "$@"; kill $$; } &
while read line; do :; done
pkill -P $!
kill $!
wait
这将不得不浪费另一个进程来等待"$@"
命令,并使用 的-P
(父 pid)选择器,pkill
因为$!
不再引用该"$@"
命令,而是引用其父进程。kill $!
如果碰巧"$@"
是内置的,仍然需要额外的。请注意,脚本中没有作业控制;所有进程(包括以 启动的进程&
)都在同一进程组中运行。
p/kill -KILL
如果TERM
信号不够,您可以在任何地方使用。
另一种更笨重的解决方法仅适用于较新(> = 4.0)版本的 bash,即使用 的非标准-t
(超时)选项并利用超时时返回 1和状态 > 128 的read
事实:read
EOF
#! /bin/bash
"$@" &
while :; do
read -t 1 line
case $? in
0) ;;
1) kill $!; wait; exit;;
*) kill -0 $! || exit;;
esac
done 2>/dev/null
这似乎也适用于mksh
。