如何等待脚本生成的所有子(和孙子等)进程



上下文:

用户为我提供了要运行的自定义脚本。这些脚本可以是任何类型的,例如用于启动多个 GUI 程序、后端服务的脚本。我无法控制脚本的编写方式。这些脚本可以是阻塞类型,即执行等待所有子进程(按顺序运行的程序)退出

#exaple of blocking script
echo "START"
first_program 
second_program 
echo "DONE"

或非阻塞类型,即在后台分叉子进程并退出类似

#example of non-blocking script
echo "START"
first_program &
second_program &
echo "DONE"

我想实现什么目标?

用户提供的脚本可以是上述两种类型中的任何一种,也可以是这两种类型的混合。我的工作是运行脚本并等待它启动的所有进程退出,然后关闭节点。如果是阻塞类型,则情况非常简单,即获取脚本执行过程的PID并等待ps -ef|grep -ef PID不再有条目。非阻塞脚本是给我带来麻烦的脚本

有没有办法获取通过执行脚本生成的所有子进程的 PID 列表?任何指示或提示将不胜感激

您可以使用

wait等待userscript启动的所有后台进程完成。由于wait仅适用于当前 shell 的子级,因此您需要获取其脚本,而不是将其作为单独的进程运行。

( source userscript; wait )

在显式子 shell 中获取脚本应该足够接近地模拟启动新进程。如果没有,您也可以将子外壳后台化,这会强制启动新进程,然后等待完成。

( source userscript; wait ) & wait

ps --ppid $PID将列出进程的所有子进程,并带有$PID

您可以打开由其他进程继承的文件描述符,然后等待它不再使用。这是一种通常工作正常的低开销方法,尽管进程可以根据需要解决它:

 foo=$(mktemp)
 ( flock -x 5000; theirscript; ) 5000> "$foo"
 flock -x 0 < "$foo"
 rm "$foo"
 echo "The script and its subprocesses are done"

您可以使用 ptrace 跟踪所有调用的进程,例如使用 strace 。这更容易,但有一些相关的开销,并且在脚本调用 suid 二进制文件时可能不起作用:

strace -f -e none theirscript

可以使用pgrep -P <parent_pid>获取子进程的列表。例:

IFS=$'n' read -ra CHILD_PROCS -d '' < <(exec pgrep -P "$1")

要获得孙子孙女,只需在每个子进程上执行相同的程序即可。

看看我的博客 Bash 函数来列出和杀死或发送信号来处理树。

您可以使用其中一个函数正确列出一个进程下生成的所有进程。每个都有自己的发送信号进行处理的方法或顺序。

唯一的限制是,这个过程仍然必须相互连接,而不是孤立的。如果您能以某种方式找到一种方法来对流程进行分组,那么这可能是您的解决方案。

简单地回答提出的问题。您可以将要调用的每个脚本的进程 ID 存储到同一变量中:

echo "START"
first_program &
child_process_ids+="$! "
second_program &
child_process_ids+="$! "
echo $child_process_ids
echo "DONE"

$child_process_ids 只是一个以空格分隔的进程 ID 字符串。现在,这回答了提出的问题,但是,我会做什么会有点不同。我会从 for 循环调用每个脚本,存储其进程 ID,然后在另一个 for 循环中等待每个脚本完成并单独检查每个退出代码。使用相同的示例,下面是它的外观。

echo "START"
scripts="first_program second_program"
for script in $scripts; do
    #Call script and send to background
    ./$script &
    #Store the script's processID that was just sent to the background
    child_process_ids+="$! "
done
for child_process_id in $child_process_ids; do
    #Pass each processId into the wait command to retrieve its exit 
    #code and store it in $rc
    wait $child_process_id
    rc=$?
    #Inspect each processes exit code
    if [ $rc -ne 0 ]; then
        echo "$child_process_id failed with an exit code of $rc"
    else
        echo "$child_process_id was successful"
    fi
done

最新更新