【问题标题】:How to wait on all child (and grandchild etc) process spawned by a script如何等待脚本产生的所有子(和孙子等)进程
【发布时间】:2013-09-10 20:58:27
【问题描述】:

上下文:

用户向我提供他们要运行的自定义脚本。这些脚本可以是任何类型的脚本,例如启动多个 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 列表?任何指针或提示将不胜感激

【问题讨论】:

  • 如果我是你,我会在启动子进程时传递一个唯一的参数,你可以稍后使用 grep 查询,而子进程会忽略该参数。
  • @user814064 感谢您的回复,但我无法控制脚本的编写方式

标签: bash shell unix process


【解决方案1】:

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

( source userscript; wait )

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

( source userscript; wait ) & wait

【讨论】:

  • 像魅力一样工作 - 谢谢@chepner
  • 在第二个sn-p中,你真的需要一个子shell吗? { source userscript; wait; } & wait 还不够吗? (& 为小组提供背景……)
  • 命令组应该没问题。除了第一个 sn-p 的背景之外,我不记得有太多的想法。
【解决方案2】:

ps --ppid $PID 将用$PID 列出该进程的所有子进程。

【讨论】:

  • 一旦主脚本退出(以微秒为单位,因为它只需要在后台启动两个进程),子进程将作为父进程分配给INIT。没有?
  • 是的。如果父级要退出,您需要先创建子 PID 列表,例如在一个文件中。
【解决方案3】:

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

 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

【讨论】:

    【解决方案4】:

    您可以使用pgrep -P &lt;parent_pid&gt; 获取子进程列表。示例:

    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 的方法,那么这可能就是您的解决方案。

    【讨论】:

    • 如果您可以将相关的代码添加到此答案中将会很有帮助(前提是您有权这样做)。
    • @sideshowbarker 好的,我更新了。我很确定基本想法足以使这个答案成为有效的答案。否则这里的其他答案也应该无效。
    【解决方案5】:

    简单地回答所提出的问题。您可以将调用的每个脚本的进程 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多