我可以在 bash 脚本中捕获时钟信号吗?

我可以在 bash 脚本中捕获时钟信号吗?

我可以在脚本中捕获 5 分钟信号吗?我想象这样的事情,

function dosomething {
   echo "It's been 5 minutes."
}

trap dosomething SIGNAL-EVERY-5-MINUTES

while true
do
    sleep 10
done

请注意,这个例子没有意义。只是问是否可以每x次捕获一些东西。


编辑:根据 @vinc17 的回答,我注意到sleep来自主 shell 的命令不会被USR1信号中断,这就是我想要做的。

#!/bin/bash
time=10

(
  set -e
  while true
  do
    sleep 30
    # Since this is run as a subshell (instead of an external command),
    # the parent pid is $$, not $PPID.
    kill -USR1 $$
  done
) &

finish() {
   kill $!
   echo "Closing."
   exit 0
}

changetime() {
   echo "Signal USR1 received."
   time=$(( $RANDOM % 8 + 1))
}

trap changetime USR1
trap finish SIGINT

while true
do
  echo "before sleep"
  sleep $time
  echo "after sleep"
done

输出,

before sleep
Signal USR1 received.
after sleep
before sleep
Signal USR1 received.
after sleep
before sleep
Signal USR1 received.
Closing.

编辑2:我编辑了上面的示例,得到了相同的输出,但又增加了一个困难:USR1陷阱函数改变了睡眠时间。所以现在,每 30 秒选择一个 1 到 8 之间的随机时间。所以,sleep当信号改变时间时,我需要从主脚本中终止。我坚持认为这没有任何意义,但我需要知道这是否可能。

答案1

您可以运行一个进程,该进程将SIGALRM每隔 x 次向 shell 脚本发送一个信号(例如 ),并对该信号使用陷阱。这个过程可以是一个执行以下操作的脚本:

set -e
while true
do
  sleep 300
  kill -ALRM $PPID
done

如果由主 shell 脚本启动。

主 shell 脚本应在不再需要时终止该进程,和/或当 pid 不再存在时该进程应终止(但是存在竞争条件)。

(已编辑)注意:如果您的主 shell 脚本使用该命令,并且是内置命令,则sleep它可能会与ALRM信号产生严重交互。sleepsleep(3)是用alarm(2)。这sleepshell 实用程序的 POSIX 描述在其理由中也说:“当睡眠被 SIGALRM 信号中断时,退出状态允许为零,因为该实用程序的大多数实现都依赖于该信号的到达来通知它们已成功达到所请求的完成时间。因此,这样的实现不能将这种情况与成功完成的情况区分开来。允许其他实现捕获信号并返回睡眠状态,直到请求的时间到期或提供正常的信号终止过程。“为了避免某些实现的潜在问题,您可以使用SIGUSR1SIGUSR2代替SIGALRM.

这是使用子 shell 的示例。为了使行为更容易看到,我将 5 分钟时间段 ( sleep 300) 替换为 5 秒时间段 ( sleep 5)。

#!/bin/sh

{
  set -e
  while true
  do
    sleep 5
    # Since this is run as a subshell (instead of an external command),
    # the parent pid is $$, not $PPID.
    kill -USR1 $$
  done
} &

trap 'echo "Signal USR1 received."' USR1

while true
do
  date
  sleep 1
done

可以使用 Ctrl-C 中断该脚本。发生这种情况时,它不会终止子 shell,但如果未重用 pid,则子 shell 会在不超过一段时间(此处为 5 秒)后自动终止,因为命令kill失败 ( kill: No such process)。

答案2

是的。最简单的方法是按照您在问题中最初描述的方式进行操作。这只是其中的一部分某物您确实应该生成另一个子进程,以便在未来 5 分钟内向您的进程发送信号。借鉴 @vinc17 的书中的一页,我也将跨度缩短到 5 秒。

trap 'ME=$$
     : $( (sleep 5
     ps -p $ME >/dev/null 2>&1 && { 
        echo hey >/dev/tty
        kill -USR1 $ME
     })&)
' USR1; kill -USR1 $$

这样你就不会遇到一些长时间运行的进程只是要求被僵尸化。相反,您有一个新的、单一用途的计数器,它在每个时间间隔都会获取对父级 PID 的新引用。我还加入了一项小检查,以确保孩子在父母离开后去世的情况下不会尝试做自己的工作。

运行它,每 5 秒你应该得到:

hey
hey
hey
hey
hey
hey
hey
hey
hey
hey

此示例也可能同时挂起终端,否则如果您尝试将整个过程置于后台,它将不会执行任何操作。这是由于它的保留/dev/tty- 如果您的意思是在后台运行整个过程,请选择一个文件,否则stty在需要时直接调用并重新执行 tty。

答案3

替换sleep $timesleep $time & wait $!,以便 USR1 陷阱将按预期运行。看回答另一个问题或 bash 手册页的 SIGNALS 章节进行解释。

在我的系统中,ALRM 信号似乎与 USR1 一样有效。

相关内容