如何定期检查STDIN是否打开?

如何定期检查STDIN是否打开?

有些程序在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.shsSTDIN关闭时,其阻塞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/busyboxksh93zsh您可以在 上设置陷阱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事实:readEOF

#! /bin/bash
"$@" &
while :; do
        read -t 1 line
        case $? in
        0)      ;;
        1)      kill $!; wait; exit;;
        *)      kill -0 $! || exit;;
        esac
done 2>/dev/null

这似乎也适用于mksh

相关内容