【问题标题】:Implementing infinite wait in shell scripting在 shell 脚本中实现无限等待
【发布时间】:2012-01-29 11:33:08
【问题描述】:

这听起来可能微不足道,但我很确定这个问题没有被问到,或者至少我找不到。

我正在寻找一种使用 shell 脚本构造 无限等待(不一定是循环)的方法,以便它永远等待并且可以被杀死(或者在技术上, 接收SIGTERM)。以下是已知的可能构造和反对它们的论据:

  1. while true; do sleep 1; done 这差不多就明白了,但是由于sleep 是外部命令,所以当我向运行脚本发送SIGTERM 时,它必须先等待sleep 完成,然后再处理信号。将sleep 1 更改为sleep 10 之类的东西,滞后会很明显。此外,该解决方案每 1 秒唤醒一次 CPU,这并不理想。
  2. while true; do read; donestdin 是 tty 时,这是完美的。 read 是一个内置的 shell,SIGTERM 立即到达脚本。但是,当stdin/dev/null 时,脚本会无助地在/dev/null 上永远运行read,从而耗尽所有CPU。

因此,需要一个永久等待的 shell 内置构造。浏览man dash 我没有找到这样的——唯一的阻塞内置函数是readwait,我不知道如何使用wait 构建一个理想的。

答案应该适用于 POSIX shell(实际上是 dash),或者不太适用于 Bash。

补充说明。

第一个示例不能完美运行的情况比我想象的要复杂。使用以下 shell 脚本:

#!/bin/sh
echo $$
while true; do
    sleep 100
done

如果你在另一个 tty 上杀死它,它会立即终止。当您尝试进行诱捕时,有趣的事情就开始了。使用此脚本:

#!/bin/sh
at_term() {
    echo 'Terminated.'
    exit 0
}
trap at_term TERM
echo $$
while true; do
    sleep 20
done

示例 1 中准确描述了发生的情况。这发生在 bash、dash 和 zsh 中。正是在这种情况下,我正在寻求一个“完美”的无限外观构造。

【问题讨论】:

  • 当我将SIGTERM 发送给dash 在循环中执行sleep 100 时,它会立即退出。
  • 你需要这个做什么?如果进程应该持续但什么都不做,它可以给自己发送一个SIGSTOP

标签: shell unix


【解决方案1】:

您可以使用命名管道进行读取:

mkfifo /tmp/mypipe
#or mknode /tmp/mypipe p

如果您稍后想向管道发送不同的任意“信号”,则可以将读取与 case 语句结合使用以采取适当的操作(甚至是有用的操作)

while read SIGNAL; do
    case "$SIGNAL" in
        *EXIT*)break;;
        *)echo "signal  $SIGNAL  is unsupported" >/dev/stderr;;
    esac
done < /tmp/mypipe

【讨论】:

  • 是的,这个几乎是完美的。我会看看是否有人能想出更简单的方法,或者我会接受......
  • 接受你的回答 :) 它在所有情况下都是通用的。
【解决方案2】:

如果你有接受浮点秒的 GNU coreutils,你可以试试:

sleep inf

这应该会阻塞,直到 64 位时间戳环绕。

【讨论】:

  • 如果你没有inf(例如在 alpine/sh 上),你总是可以把它放在一个无限循环中。无论哪种方式,这都是一种享受
  • 如果只有 shell 脚本(而不是 sleep(1) 进程)接收到信号,这将不起作用。
【解决方案3】:

这是一个没有循环的解决方案:

#!/usr/local/bin/dash

echo $$

# -$$: kill process group (parent and children)
#trap 'trap - TERM; kill 0' TERM
#trap 'trap - INT TERM; kill 0' INT TERM

trap 'trap - TERM; kill -s TERM -- -$$' TERM

tail -f /dev/null & wait

exit 0

【讨论】:

  • 你的脚本很神奇!它是唯一能做我想做的事(响应信号 15)。你能详细解释一下吗?那你为什么在陷阱中重复陷阱? trap 中写的全部内容我都不清楚,为什么 & after tail,为什么还要等待?
  • @MohammedNoureldin trap - TERM 部分删除了脚本的现有陷阱处理程序,因此不会再次捕获相同的信号。 kill -s TERM -- -$$ 部分将SIGTERM 发送到同一进程组中的所有进程。实际上是所有子进程和后代进程(包括脚本本身)。 IE。就kill 而言,负PID 是PGID,但我们需要额外的-- 以免与命令开关混淆。由于SIGTERM 处理程序已被删除,脚本将在接收到第二个SIGTERM 信号(它在旧处理程序中发送)时终止。
【解决方案4】:

您的第二个选项有什么问题,但强制它从 stdin 读取? (需要bash

while true; do
  read
done < /dev/stdin

来自man bash

Bash 在使用时会特别处理多个文件名 重定向,如下表所述:

          /dev/stdin
                 File descriptor 0 is duplicated.

【讨论】:

  • 我找不到太多关于 /dev/stdin 的文档,因此有理由怀疑它可能并不总是可用。
  • 如果有人能提供一些关于 /dev/stdin 的文档,那就太好了 :)
  • 标准输入重定向到 /dev/null 时会中断。将其放入 a.sh 并运行 bash a.sh &lt; /dev/null 并查看 CPU 被吃掉...
【解决方案5】:
#!/bin/sh
at_term() {
  echo 'Terminated.'
  exit 0
}
trap at_term TERM
echo $$
while true; do
  sleep 20 &
  wait $!
done

【讨论】:

  • 感谢您的解决方案!个人觉得比使用read更优雅,而且效果很好!
【解决方案6】:

发送给进程的SIGTERM由内核传递给进程,无论它是否处于睡眠状态。

尝试一下,也许像这样(bash 示例)

sleep 20 &
kill $! && fg

【讨论】:

    【解决方案7】:

    为什么不使用sleep forever?这是为了永远阻塞而设计的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-16
      相关资源
      最近更新 更多