我正在编写一个 bash 脚本,它会生成几个子进程,并由一个cron
作业每小时运行一次。它实际上是rsync
向远程服务器运行命令以及相关ssh
连接。
如果 rsync 命令或脚本耗时过长,我想终止它及其子进程,但首先要给它一个宽限期。该脚本每小时运行一次,但接下来的两次尝试将看到该脚本的上一个实例正在运行,并在尝试开始传输之前退出。第三次后续尝试将终止仍在运行的原始实例,然后再开始新的传输。
我决定通过写入 PID 文件来控制这一点,第一个实例将其 PID 写入文件,接下来的两次尝试在退出之前将其 PID 写入文件。第三次尝试会杀死第一个,然后用新的 PID 覆盖 PID 文件。任何成功完成都会擦除 PID 文件。为了杀死所有子项,我决定使用原始脚本实例的 PGID,我通过以下方式获得:
previous_pid=$(head -n1 "$pid_file")
previous_groupid=$(ps -hp "$previous_pid" -o pgid:1)
然后用以下方法杀死它:
kill -- -"$previous_pid"
我担心的是,可能存在这样的情况:原始脚本在没有清理 PID 文件的情况下就死掉了,而且 PID 被重用,同时也重用了 PGID,所以我最终会终止一个完全不相关的进程。我该如何避免这种情况?
答案1
让脚本自行终止。例如,在脚本开头添加如下内容:
kill_myself () {
while sleep 1
do
if [[ -f /tmp/kill-myself ]]
then
rm /tmp/kill-myself
kill -- -$1
fi
done
}
kill_myself $$ &
现在,时机成熟了:
touch /tmp/kill-myself
您可以自定义它,这样脚本的每个实例都有自己的终止文件,您可以将其记录在某处。然后您将使用该文件终止该脚本的特定实例。
另一种方法:您可能可以使用pgrep
/pkill
进行更精细的匹配。它们可以从 pid 文件中读取,与命令名称和参数进行匹配等。
类似下面的操作只会终止与您的脚本同名的进程(假设您以script-name
或 的形式调用它/path/to/script-name
):
pkill -g "$previous_groupid" script-name
如果您这样做,这将不起作用bash script-name
。或者只是终止命令rsync
,这可能会让您的脚本在那之后退出:
pkill -g "$previous_groupid" rsync