在Bash中设置命令超时
1. 简介
即使是最好的计算机也具有有限的处理能力。这意味着进程不能永远运行。为了避免他们尝试这样做的情况,我们可以使用超时。
在本教程中,我们首先讨论什么是超时。接下来,我们谈谈一些超时条件。最后,我们将探讨在 Bash 中使用超时的不同方式。特别是,我们描述了一种标准和一种定制解决方案。
我们使用 GNU Bash 5.1.4 在 Debian 11 Bullseye 上测试了代码。它符合 POSIX 并且应该在任何这样的环境中工作。
2. 超时
超时存在于许多级别的计算中。在所有情况下,超时都有相同的目的:限制进程的运行时间。虽然我们强制超时的确切方式各不相同,但基本思想是相同的。
首先,我们决定一个超时值。接下来,我们注意进程的开始时间。然后我们等待超时值并检查进程是否正在运行。如果是,我们发送一个终止信号 来停止它。如果该过程自行完成,我们将停止监控。
从广义上讲,任何行动都可以在时间上受到限制,但它对某些人来说比其他人更有意义。选择通常取决于超时条件的概率。
3. 超时条件
超时可能有很多原因。通常,许多属于几个类别之一。让我们来看看它们。
3.1. 硬件故障
由于硬件组件的问题,任何过程都可能无法完成。例如,**内存错误会产生不可读的数据和文件。**因此,可能会因为这些文件或数据而无法完成流程。另一个例子是损坏的网络电缆停止回复。
3.2. 硬件规格
即使没有错误,硬件也可能不够强大,无法在合理的时间内完成一个过程。**例如,内存不足可能是速度变慢甚至抖动 **的原因。此外,缓慢的网络连接可能会不合理地延迟进程。
3.3. 发展
这通常是最广泛的超时条件类别。事实上,在环境之外,我们如何实现一个流程对其运行时间和限制有着最显着的影响。
重要的是,运行时出错可能会导致意外超时。部分地,这意味着我们应该考虑不同的硬件。
另一个常见问题是死锁 。它们发生在两个进程相互等待完成时。因此,它们都没有完成,它们会无限期地等待。
当然,即使在最简单的程序中也可能存在问题。实际上,单个线程中的简单无限循环可以阻止进程完成。 还有多种其他可能性,但这些是一般可能性。接下来,我们将讨论如何在 Bash 中处理它们。
4. 重击超时
在 Bash 中使用超时有不同的方法。让我们看看两大类的解决方案——标准的和定制的。
4.1. 超时命令
显然,我们可以根据需要使用特定工具。例如,有*timeout 命令,它是GNU coreutils 包的一部分。 ** timeout命令在超时期限后停止执行的进程*:
$ timeout 1s bash -c 'for((;;)); do :; done'
$ echo $?
124
在这里,我们运行一个无限循环。我们将超时时间设置为一秒,然后timeout应该终止进程。
重要的是,请注意退出代码 。这意味着该进程已使用默认的SIGTERM 信号停止。我们可以使用*–signal选项选择另一个信号。 此外,–preserve-status开关可以强制超时*返回原始进程的状态码:
$ timeout --preserve-status 1s bash -c 'exit 66'
$ echo $?
66
使用此选项,我们可以检查导致问题的代码。
timeout命令是外部包的一部分。这意味着它可能并非在每个系统上都可用。
4.2. 自定义超时
有时我们无法访问自定义命令,或者只是不想使用它们。在这种情况下,我们可以创建自己的超时命令版本:
function xtimeout {
timeout=$1
shift
command=("[[email protected]](/cdn_cgi/l/email_protection)")
(
"${command[@]}" &
runnerpid=$!
trap -- '' SIGTERM
( # killer job
sleep $timeout
if ps -p $runnerpid > /dev/null; then
kill -SIGKILL $runnerpid 2>/dev/null
fi
) &
killerpid=$!
wait $runnerpid
kill -SIGKILL $killerpid
)
}
此函数期望以秒为单位的超时作为其第一个参数。然后它会删除第一个参数并使用其余的作为命令行来执行。
首先,我们执行命令。实际上,它在子shell 的后台运行。这是为了静音任何后台作业消息。接下来,我们的函数使用*sleep *来等待指定的秒数。最后,我们检查启动命令的PID 。如果它正在运行,杀手的工作会阻止它。如果命令在超时用完之前完成,我们会停止杀手作业。
当然,此功能不如timeout命令灵活或健壮。但是,它的所有部分都可以在任何标准 Linux 机器上使用。