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

时间:2013-09-06 17:18:41

标签: bash shell unix process

上下文

用户为我提供了自定义脚本来运行。这些脚本可以是任何类型的脚本,以启动多个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"

我想要实现的目标是什么?

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

有没有办法可以获得执行脚本所产生的所有子进程的PID列表?任何指针或提示都将受到高度赞赏

5 个答案:

答案 0 :(得分:7)

ps --ppid $PID将使用$PID列出流程的所有子流程。

答案 1 :(得分:6)

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

( source userscript; wait )

在显式子shell中获取脚本应该模拟足够紧密地启动新进程。如果没有,您还可以设置子shell,这会强制启动一个新进程,然后等待 it 完成。

( source userscript; wait ) & wait

答案 2 :(得分:4)

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

 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

答案 3 :(得分:1)

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

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

为了得到大孩子,只需在每个子进程上执行相同的操作。

查看我的博客Bash functions to list and kill or send signals to process trees

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

这些唯一的限制是进程仍然必须连接而不是孤立的。如果你能以某种方式找到group your processes的方法,那么这可能是你的解决方案。

答案 4 :(得分:1)

简单回答被问到的问题。您可以将您正在调用的每个脚本的进程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,然后在另一个脚本中等待循环完成并单独检查每个退出代码。使用相同的例子,这就是它的样子。

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