杀死所有超过一定年龄的过程
1. 概述
在某些情况下,我们可能想要杀死已经运行了很长时间的进程。例如,我们可能期望一个进程在一定时间内完成它的任务。但是,如果该过程花费的时间比预期的要长,则可能是因为发生了错误。
在本教程中,我们将学习如何杀死超过某个时间的进程。为此,我们将学习三个替代方案。一个涉及使用killall命令,另一个涉及使用pkill命令,最后一个涉及使用ps命令。
2. 使用killall
杀死超过特定年龄的进程的一种简单方法是使用killall 命令。该命令将我们要杀死的进程的名称作为参数。
要仅杀死超过某个年龄的进程,我们必须添加-o*参数并指定所需的年龄。* -o参数将时间量作为参数,时间量可以采用不同的单位。时间单位可以是 s、m、h、d、w、M 或 y,表示秒、分钟、小时、天、周、月和年。
让我们杀死所有名为ping 超过 12 小时的进程:
$ killall -o 12h ping
当我们运行上面的命令时,killall会向所有运行超过 12 小时的ping命令发送SIGTERM信号。如果我们认为进程不会因SIGTERM信号而终止,我们可以使用其他信号 终止进程。例如,如果我们认为进程挂起并且没有响应命令,就会发生这种情况。
因此,我们可以使用参数*-SIGNAL* 更改信号。例如,要使用SIGKILL,我们必须使用参数*-SIGKILL*。让我们使用SIGKILL信号杀死所有名为netcat 的进程超过 1 天:
$ killall -o 1d -SIGKILL netcat
到目前为止,我们已经学会了如何杀死具有特定名称的进程。但是,如果我们添加*-r*参数, killall可以将名称解释为正则表达式。这允许我们杀死具有不同名称的进程。
假设我们有几个 bash 脚本正在运行,我们只想杀死那些运行超过 2 小时的脚本。然后,如果所有这些 bash 脚本都是以 .sh 结尾的文件,我们可以使用*.sh$*正则表达式。让我们尝试一下:
$ killall -o 2h -r '\.sh$'
这样,我们只杀死以*.sh*结尾且超过 2 小时的进程。
3. 使用pkill
我们也可以使用pkill 来终止长时间运行的进程。pkill命令类似于killall命令,但名称过滤器始终是正则表达式。由于pkill命令的行为方式与*pgrep 相同,因此我们可以使用pgrep并使用与pkill*相同的参数。
这样,我们可以在实际执行之前检查哪些进程将被杀死。*当我们使用pkill杀死进程时,我们可以添加-O参数后跟以秒为单位的年龄。通过这样做,我们只杀死比那个年龄更老的进程。**让我们杀死所有名为ping*的进程超过 43200 秒(12 小时):
$ pkill -x -O 43200 ping
请注意,我们使用了*-x参数。此参数告诉pkill杀死与名称“ ping ”完全匹配的所有进程。因此,该示例会杀死所有名为ping的进程,而不会杀死具有相似名称的其他进程,例如arping或ping6*。与killall类似,pkill默认发送SIGTERM信号。但是,我们可以使用参数*-SIGNAL更改pkill发送的信号。**这次让我们使用SIGKILL*信号重复前面的示例:**
$ pkill -SIGKILL -x -O 43200 ping
最后,我们也可以使用正则表达式。在这种情况下,我们不必像使用killall那样添加任何额外的参数。让我们杀死所有以*.sh*结尾的超过 3600 秒(1 小时)的进程:
$ pkill -O 3600 '\.sh$'
请注意,这次我们不使用*-x参数,因为在这种情况下,pkill将匹配确切的模式“.sh”* ,而不是包含以*“.sh”*结尾的名称。
4. 使用ps
最后,我们还可以使用ps 命令结合awk 和kill 命令来杀死超过某个年龄的进程。在这种情况下,我们将首先使用ps列出进程,并使用awk根据它们的年龄和名称过滤它们。最后,我们将使用kill来终止它们。
4.1. 在线进程
使用ps命令,我们可以选择要查看的列。首先,我们使用*-e参数选择所有进程。然后我们添加-o参数,后跟所需的列。**我们可以使用etimes列来获取自进程开始以来的时间(以秒为单位)。**让我们尝试选择列pid*、etimes和cmd,因此我们可以看到 PID、以秒为单位的年龄以及每个进程的命令:
$ ps -eo pid,etimes,cmd
PID ELAPSED CMD
1 148168 init [3]
...
1703 148869 sshd: /usr/sbin/sshd
5223 4751 screen
5239 4750 -/bin/bash
6135 1942 cmus
23817 94840 cmus
25405 120136 /usr/lib64/firefox/firefox-bin
27613 0 ps -eo pid,etimes,cmd
29739 119782 cmus
...
当我们只想查看某些进程在不杀死它们的情况下运行了多长时间时,这也很有用。现在,我们可以将ps输出通过管道传输到awk 以过滤超过某个年龄的进程。我们必须比较第二列,看看它是否大于期望的年龄。
让我们使用awk仅列出超过 7200 秒(2 小时)的进程:
$ ps -eo pid,etimes,cmd | awk '$2 > 7200'
1 148168 init [3]
...
1703 148880 sshd: /usr/sbin/sshd
23817 94851 cmus
25405 120147 /usr/lib64/firefox/firefox-bin
29739 119793 cmus
...
如我们所见,我们列出了所有运行超过 2 小时的进程。但是,我们不想杀死所有人。例如,我们不应该杀死像init或systemd这样的进程。
因此,我们可以向awk添加更多过滤器,以仅选择我们想要杀死的进程。我们可以通过比较第三列来做到这一点。让我们选择所有超过 7200 秒的名为“ cmus ”的进程:
$ ps -eo pid,etimes,cmd | awk '$2 > 7200 && $3 == "cmus"'
23817 94879 cmus
29739 119710 cmus
最后,我们可以编写一个名为filter_processes_older_than的函数 ,并通过参数接收年龄和名称。我们开始做吧:
$ filter_processes_older_than() {
ps -eo pid,etimes,cmd | awk '$2 > '$1' && $3 == "'$2'"'
}
$ filter_processes_older_than 7200 cmus
23817 94883 cmus
29739 119724 cmus
4.2. 杀死进程
现在我们有了要杀死的进程列表,我们可以将xargs与kill一起使用。
首先,我们要得到每个进程的PID。因此,我们可以编写一个名为filter_pids_older_than的新函数来仅打印每个进程的 PID。
这样,我们可以保持我们的filter_processes_older_than函数完好无损,以验证哪些进程将被杀死。让我们编写filter_pids_older_than函数来仅打印第一列中的 PID:
$ filter_pids_older_than() {
filter_processes_older_than "$1" "$2" | awk '{print $1}'
}
$ filter_pids_older_than 7200 cmus
23817
29739
现在,为了终止这些进程,我们将该输出通过管道传输到xargs kill:
$ filter_pids_older_than 7200 cmus | xargs kill
这样,我们就杀死了所有运行超过 7200 秒的cmus进程。我们可以通过将*-SIGNAL参数添加到kill来更改用于终止进程的信号。因此,让我们编写一个名为kill_pids_older_than*的新函数,并使用第三个可选参数来指定信号:
$ kill_pids_older_than() {
filter_pids_older_than "$1" "$2" | xargs kill -${3:-SIGTERM}
}
我们可以注意到我们使用了 ${3:-SIGTERM}。这将仅在定义第三个参数时使用。如果未定义,则使用默认值SIGTERM。
最后,我们可以一起测试这一切。
让我们首先验证哪些进程会被filter_processes_older_than函数杀死,然后我们使用SIGKILL信号通过kill_pids_older_than函数杀死它们:
$ filter_processes_older_than 7200 cmus
23817 94955 cmus
29739 119796 cmus
$ kill_pids_older_than 7200 cmus SIGKILL