如何在 Linux 上使用 timeout 命令

计算机资源是有限的。为了有效管理进程,我们可以使用 timeout 命令来限制其运行时间。本文将深入探讨如何运用此命令来控制程序的执行时长。

超时命令的优势

timeout 命令允许您为程序设定一个最长运行时间。那么,为什么要这样做呢?

首先,当您明确知道程序需要运行多久时,超时命令非常有用。例如,对于日志记录或数据捕获程序,您可以设置一个超时,以防止日志文件无限增长,占用硬盘空间。

其次,即使您不确定程序需要运行多久,但您不希望它无限期地运行,超时命令也很有价值。有时候,我们可能会启动一个程序,最小化终端窗口,然后就忘记了它。

一些程序,包括简单的工具,可能会产生影响网络性能的网络流量。或者,它们可能会消耗目标设备的资源,导致性能下降。(ping 命令就是一个例子。)长时间运行这些类型的程序是不明智的。

timeout 命令是 GNU核心实用程序 的一部分,这意味着像 Linux 和 macOS 这样的类 Unix 操作系统都内置了此功能。无需额外安装,您可以直接使用。

超时命令的基本用法

下面是一个简单的例子。 默认情况下,ping 命令会一直运行,直到您按下 Ctrl+C 手动停止它。 如果您不中断它,它就会继续发送网络请求。

ping 192.168.4.28

通过使用 timeout 命令,我们可以确保 ping 不会无限期地运行,从而避免占用网络带宽或对目标设备造成负担。

下面的命令使用 timeout 来限制 ping 的运行时间。 我们允许 ping 运行 15 秒:

timeout 15 ping 192.168.4.28

15 秒后,timeout 将终止 ping 会话,并返回到命令行提示符。

使用不同的时间单位

请注意,我们不必在数字 15 后面添加 “s”。 timeout 默认以秒为单位。 您可以添加 “s”,但没有实际区别。

要使用分钟、小时或天作为时间单位,请添加 “m”、”h” 或 “d”。例如,要让 ping 运行 3 分钟,请使用以下命令:

timeout 3m ping 192.168.4.28

ping 将运行三分钟,然后 timeout 会介入并停止 ping 会话。

使用超时限制数据捕获

一些数据捕获文件可能会迅速膨胀。为了防止此类文件变得过大,请限制允许捕获程序运行的时间。

在这个例子中,我们使用 tcpdump,一个网络流量捕获工具。 在一些 Linux 发行版中,如 Ubuntu 和 Fedora,tcpdump 已经预装好了。 如果您的系统中没有安装,您可能需要使用以下命令进行安装(例如,在 Manjaro 和 Arch Linux 中):

sudo pacman -Syu tcpdump

我们可以使用默认选项运行 tcpdump 10 秒,并将其输出重定向到名为 capture.txt 的文件:

timeout 10 sudo tcpdump > capture.txt

tcpdump 本身具有将捕获的网络流量保存到文件的选项。这只是一个简单的演示,因为我们主要关注 timeout 命令。)

tcpdump 开始捕获网络流量,我们等待 10 秒。 10 秒过去后,tcpdump 仍在运行,capture.txt 的大小仍在增长。 需要手动使用 Ctrl+C 来停止 tcpdump

使用 ls 命令检查 capture.txt 的大小,发现它在几秒钟内增长到了 209K。 文件增长速度惊人!

ls -lh capture.txt

怎么回事?为什么 timeout 没有停止 tcpdump

这与信号有关。

理解信号

timeout 想要停止程序时,它会发送一个SIGTERM 信号。这是一种礼貌地请求程序终止的方式。 然而,有些程序可能会选择忽略 SIGTERM 信号。 当这种情况发生时,我们需要让 timeout 更强硬一些。

我们可以请求 timeout 发送 SIGKILL 信号来代替。

SIGKILL 信号无法被“捕获、阻止或忽略” – 它总是能强制程序终止。 与 SIGTERM 的礼貌请求不同,SIGKILL 是一种更直接的强制手段。

我们可以使用 -s (signal) 选项来告诉 timeout 发送 SIGKILL 信号:

timeout -s SIGKILL 10 sudo tcpdump > capture.txt

这一次,只要 10 秒过去,tcpdump 就会停止。

先尝试礼貌方式

我们可以请求 timeout 先尝试使用 SIGTERM 停止程序,只有在 SIGTERM 不起作用时才发送 SIGKILL

为此,我们使用 -k (kill after) 选项。 -k 选项需要一个时间值作为参数。

在下面的命令中,我们要求 timeoutdmesg 运行 30 秒,然后使用 SIGTERM 信号终止它。 如果 40 秒后 dmesg 仍在运行,则表示 SIGTERM 被忽略,timeout 将发送 SIGKILL 来完成任务。

dmesg 是一个实用程序,可以 监视内核环形缓冲区消息 并将其显示在终端窗口中。

timeout -k 40 30 dmseg -w

dmesg 运行 30 秒,并在收到 SIGTERM 信号时停止。

我们知道阻止 dmesg 的不是 SIGKILL,因为 SIGKILL 总会在终端窗口中留下一个词的讣告:“Killed”。 在这种情况下,并没有发生这种情况。

获取程序的退出代码

表现良好的程序在终止时会向 shell 返回一个值,称为退出代码。这通常用于告诉 shell(或启动程序的任何进程)该程序在运行时是否遇到问题。

timeout 也有自己的退出代码,但我们可能并不关心。 我们可能对 timeout 控制的程序的退出代码更感兴趣。

下面的命令让 ping 运行 5 秒钟。它正在 ping 一台名为 Nostromo 的计算机,这台计算机位于用于研究本文的测试网络上。

timeout 5 ping Nostromo.local

该命令运行 5 秒钟,然后 timeout 将其终止。 我们可以使用以下命令检查退出代码:

echo $?

退出代码是 124。这是 timeout 用于指示程序使用 SIGTERM 终止的值。 如果 SIGKILL 终止了程序,则退出代码为 137。

如果我们使用 Ctrl+C 中断程序,timeout 的退出代码为零。

timeout 5 ping Nostromo.local
echo $?

如果程序在 timeout 终止之前自行结束,那么 timeout 可以将程序的退出代码传递回 shell。

为此,程序必须自行停止(即,它不会被超时终止),并且我们必须使用 --preserve-status 选项。

如果我们使用值为 5 的 -c (count) 选项,ping 将只发送 5 个请求。如果我们给 timeout 一分钟的持续时间,ping 肯定会自行终止。 然后,我们可以使用 echo 检查退出值。

timeout --preserve-status 1m ping -c 5 Nostromo.local
echo $?

ping 完成其 5 个 ping 请求并终止。 退出代码为零。

为了验证退出代码是否来自 ping,我们可以强制 ping 生成不同的退出代码。如果我们尝试向不存在的 IP 地址发送 ping 请求,ping 将失败并返回错误退出代码。 然后,我们可以使用 echo 来检查退出代码是否非零。

timeout --preserve-status 1m ping -c 5 NotHere.local
echo $?

ping 命令显然无法到达不存在的设备,因此它会报告错误并关闭。 退出代码是 2。这是 ping 用于一般错误的退出代码。

总结

timeout 命令为正在运行的程序提供了一种界限。如果存在日志文件可能超出硬盘驱动器容量的风险,或者担心忘记正在运行的网络工具,请使用 timeout 命令来限制其运行时间,让您的计算机自行管理。