bash 如何等待脚本产生的所有子(和孙子等)进程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18663196/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to wait on all child (and grandchild etc) process spawned by a script
提问by Ram
Context:
语境:
Users provide me their custom scripts to run. These scripts can be of any sort like scripts to start multiple GUI programs, backend services. I have no control over how the scripts are written. These scripts can be of blocking type i.e. execution waits till all the child processes (programs that are run sequentially) exit
用户向我提供他们的自定义脚本来运行。这些脚本可以是任何类型的脚本,例如启动多个 GUI 程序、后端服务的脚本。我无法控制脚本的编写方式。这些脚本可以是阻塞类型,即执行等待直到所有子进程(顺序运行的程序)退出
#exaple of blocking script
echo "START"
first_program
second_program
echo "DONE"
or non blocking type i.e. ones that fork child process in the background and exit something like
或非阻塞类型,即在后台分叉子进程并退出类似的类型
#example of non-blocking script
echo "START"
first_program &
second_program &
echo "DONE"
What am I trying to achieve?
我想要达到什么目标?
User provided scripts can be of any of the above two types or mix of both. My job is to run the script and wait till all the processes started by it exit and then shutdown the node. If its of blocking type, case is plain simple i.e. get the PID of script execution process and wait till ps -ef|grep -ef PID has no more entries. Non-blocking scripts are the ones giving me trouble
用户提供的脚本可以是上述两种类型中的任何一种,也可以是两者的混合。我的工作是运行脚本并等待它启动的所有进程退出,然后关闭节点。如果它是阻塞类型,情况很简单,即获取脚本执行进程的 PID 并等待 ps -ef|grep -ef PID 没有更多条目。非阻塞脚本给我带来了麻烦
Is there a way I can get list of PIDs of all the child process spawned by execution of a script? Any pointers or hints will be highly appreciated
有没有办法获得通过执行脚本产生的所有子进程的 PID 列表?任何指针或提示将不胜感激
采纳答案by chepner
You can use wait
to wait for all the background processes started by userscript
to complete. Since wait
only works on children of the current shell, you'll need to source their script instead of running it as a separate process.
您可以使用wait
等待所有启动的后台进程userscript
完成。由于wait
仅适用于当前 shell 的子进程,因此您需要获取其脚本的源代码,而不是将其作为单独的进程运行。
( source userscript; wait )
Sourcing the script in an explicit subshell should simulate starting a new process closely enough. If not, you can also background the subshell, which forces a new process to be started, then wait for itto complete.
在显式子 shell 中获取脚本应该足够接近地模拟启动一个新进程。如果没有,您还可以将子shell 设为后台,这会强制启动一个新进程,然后等待它完成。
( source userscript; wait ) & wait
回答by Ansgar Wiechers
ps --ppid $PID
will list all child processes of the process with $PID
.
ps --ppid $PID
将列出进程的所有子进程$PID
。
回答by that other guy
You can open a file descriptor that gets inherited by other processes, and then wait until it's no longer in use. This is a low overhead method that usually works fine, though it's possible for processes to work around it if they want:
您可以打开一个被其他进程继承的文件描述符,然后等到它不再被使用。这是一种低开销的方法,通常可以正常工作,但如果进程需要,可以解决它:
foo=$(mktemp)
( flock -x 5000; theirscript; ) 5000> "$foo"
flock -x 0 < "$foo"
rm "$foo"
echo "The script and its subprocesses are done"
You can follow all invoked processes using ptrace, such as with strace
. This is easier, but has some associated overhead and may not work when scripts invoke suid binaries:
您可以使用 ptrace 跟踪所有调用的进程,例如使用strace
. 这更容易,但有一些相关的开销,并且在脚本调用 suid 二进制文件时可能不起作用:
strace -f -e none theirscript
回答by konsolebox
You can use pgrep -P <parent_pid>
to get a list of child processes. Example:
您可以使用pgrep -P <parent_pid>
来获取子进程列表。例子:
IFS=$'\n' read -ra CHILD_PROCS -d '' < <(exec pgrep -P "")
And to get the grand-children, simply do the same procedure on each child process.
要获得孙子进程,只需对每个子进程执行相同的程序即可。
Check out my blog Bash functions to list and kill or send signals to process trees.
查看我的博客Bash 函数以列出和终止或发送信号以处理树。
You can use one of those function to properly list all processes spawned under one process. Each has their own method or order of sending signals to process.
您可以使用这些功能之一正确列出在一个进程下产生的所有进程。每个都有自己的方法或发送信号进行处理的顺序。
The only limitation by those is that process still have to be connected and not orphaned. If you could somehow find a way to group your processes, then that might be your solution.
回答by Jason Slobotski
To simply answer the question that was asked. You could store the process ID of each script you're calling into the same variable:
简单地回答被问到的问题。您可以将调用的每个脚本的进程 ID 存储到同一个变量中:
echo "START"
first_program &
child_process_ids+="$! "
second_program &
child_process_ids+="$! "
echo $child_process_ids
echo "DONE"
$child_process_ids would just be a space delimited string of process Ids. Now, this answers the question asked, however, what I would do would be a bit different. I would call each script from a for loop, store its process ID, then wait on each one in another for loop to finish and inspect each exit code individually. Using the same example, here's what it would look like.
$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