Bash 脚本用于在更高级别捕获 ctrl+c,而无需中断前台任务

Bash 脚本用于在更高级别捕获 ctrl+c,而无需中断前台任务

我正在尝试编写一个可以在其中执行命令的 bash 脚本tail -F /tmp/foo.txt |& grep -E "error"

我希望能够Ctrl+c正在运行的脚本并使用陷阱或其他机制来拦截脚本内的 SIGINT 信号没有扰乱指挥。

我已经调查了描述的陷阱和作业控制示例这里事实上这个例子说的是

类似地,您可以在不受 Ctrl+c 限制的脚本中,从受 Ctrl+c 限制的函数内部运行不受 Ctrl+c 限制的命令。这完全取决于您在哪个(子)shell 中调用了什么陷阱。

然而,我还没有在实践中做到这一点。如果我让命令不受Ctrl+的影响c,那么我将永远不会在 bash 的更高级别收到 SIGINT。

下面是一个 sleep 调用的示例,它似乎表明事情确实(有点)有效,但不如预期

#!/bin/bash

trap 'echo "trap worked"' INT

(
   trap '' INT
   sleep 5
   echo "done"
)

结果是:

^Cdone
trap worked

因此在这种情况下,sleep调用必须完成,然后在子 shell 完成后调用陷阱。如果我用/行替换sleep调用,那么子 shell 将永远不会退出,因为 SIGINT 被阻止,而更高级别的 bash 似乎直到子 shell 完成后才收到 SIGINT。tailgrep

我该如何在 bash 中执行此操作?我并不喜欢使用陷阱。

答案1

其他答案解释了为什么您trap worked只在之后看到done。如果您在后台运行子 shell,然后wait为其运行,结果将有所不同:

#!/bin/bash

trap 'echo "trap worked"' INT

(
   trap '' INT
   sleep 5
   echo "done"
) &

wait

如果在子 shell 运行时按下Ctrl+ c,它将立即触发陷阱。它还将使脚本继续执行wait并完成。wait如果不想让脚本完成,请循环运行:

until wait; do :; done

这样你将能够轻松地多次触发陷阱,并且脚本将在子shell退出后退出。

我猜这并不是你所说的“不中断前台任务”的确切意思,因为任务(此处sleep:)是在后台运行的,它会产生后果。当禁用作业控制时(默认情况下在脚本中禁用),&将 stdin 重定向到/dev/null或等效文件。这样,你在后台运行的内容就不会窃取输入。如果你想让我们的后台子 shell 能够从整个脚本的 stdin 中读取,那么你需要明确将 stdin 重定向到 stdin(<&0)。它看起来像一个无操作,但&实际上它是有意义的,它使异步作业的行为更像前台作业:

(…) <&0 &

还有一件事:wait不带参数的注释会等待所有当前活动的子进程。如果您的实际脚本在后台运行更多作业,并且您不想wait等待其中一些作业,最简单的方法可能是disown


示例代码:

#!/bin/bash

trap 'echo "trap worked"' INT

(
   trap '' INT
   sleep 10
   echo "(not waited for) done"
) &
disown "$!"

(
   trap '' INT
   cat
   echo "(waited for) done"
) <&0 &

until wait; do :; done

笔记:

  • cat并且sleep都对Ctrl+免疫c
  • 您可以多次点击Ctrl+并触发陷阱。c
  • 同时,您可以写入行cat,它也会工作,即,它会打印出来Enter(但如果您在一行中间Ctrl+ ,那么您到目前为止输入的内容(即尚未交付的内容)将被丢弃并且永远不会交付,因为这是线路纪律的工作方式)。ccatcat
  • 出口cat Ctrl+d在完成之前sleep 10,您将看到脚本不会等待被放弃的工作。

附注:在使用 SIGINT 和 bash 时,最好知道 bash 在处理 SIGINT/SIGQUIT 传递时实施了“等待和协作退出”方法。请参阅此链接本文

相关内容