bash 在退出脚本之前等待后台进程完成

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14254118/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:10:12  来源:igfitidea点击:

Waiting for background processes to finish before exiting script

bashshellunixtclgrep

提问by egorulz

How do I make sure that all my background processes have finished execution before I exit my script (TCL/Bash).

在退出脚本 (TCL/Bash) 之前,如何确保所有后台进程都已完成执行。

I was thinking of writing all my background process pids to a pidfile. And then at the end pgrep the pidfile to see if any processes are still running before I exit.

我正在考虑将我所有的后台进程 pid 写入 pidfile。然后在最后 pgrep pidfile 以查看在我退出之前是否有任何进程仍在运行。

Is there some simpler way to do this? And is there a TCL specific way to do this?

有没有更简单的方法来做到这一点?是否有 TCL 特定的方法来做到这一点?

回答by William Pursell

If you want to wait for jobs to finish, use wait. This will make the shell wait until all background jobs complete. However, if any of your jobs daemonize themselves, they are no longer children of the shell and wait will have no effect (as far as the shell is concerned, the child is already done. Indeed, when a process daemonizes itself, it does so by terminating and spawning a new process that inherits its role).

如果要等待作业完成,请使用wait. 这将使 shell 等待所有后台作业完成。但是,如果您的任何作业自己守护进程,它们就不再是 shell 的子进程并且 wait 将不起作用(就 shell 而言,子进程已经完成。确实,当一个进程自我守护时,它会这样做通过终止并产生一个继承其角色的新进程)。

#!/bin/sh
{ sleep 5; echo waking up after 5 seconds; } &
{ sleep 1; echo waking up after 1 second; } &
wait
echo all jobs are done!

回答by Suku

You can use kill -0for checking whether a particular pid is running or not.

您可以kill -0用于检查特定 pid 是否正在运行。

Assuming, you have list of pidnumbers in a file called pidin pwd

假设您在 pwd 中pid调用的文件中有数字列表pid

while true;
do 
    if [ -s pid ] ; then
        for pid in `cat pid`
        do  
            echo "Checking the $pid"
            kill -0 "$pid" 2>/dev/null || sed -i "/^$pid$/d" pid
        done
    else
        echo "All your process completed" ## Do what you want here... here all your pids are in finished stated
        break
    fi
done

回答by Hai Vu

WARNING: Long script ahead.

警告:前面的脚本很长。

A while ago, I faced a similar problem: from a Tcl script, launch a number of processes, then wait for all of them to finish. Here is a demo script I wrote to solve this problem.

不久前,我遇到了类似的问题:从 Tcl 脚本启动多个进程,然后等待所有进程完成。这是我为解决此问题而编写的演示脚本。

main.tcl

主文件

#!/usr/bin/env tclsh

# Launches many processes and wait for them to finish.
# This script will works on systems that has the ps command such as
# BSD, Linux, and OS X

package require Tclx; # For process-management utilities

proc updatePidList {stat} {
    global pidList
    global allFinished

    # Parse the process ID of the just-finished process
    lassign $stat processId howProcessEnded exitCode

    # Remove this process ID from the list of process IDs
    set pidList [lindex [intersect3 $pidList $processId] 0]
    set processCount [llength $pidList]

    # Occasionally, a child process quits but the signal was lost. This
    # block of code will go through the list of remaining process IDs
    # and remove those that has finished
    set updatedPidList {}
    foreach pid $pidList {
        if {![catch {exec ps $pid} errmsg]} {
            lappend updatedPidList $pid
        }
    }

    set pidList $updatedPidList

    # Show the remaining processes
    if {$processCount > 0} {
        puts "Waiting for [llength $pidList] processes"
    } else {
        set allFinished 1
        puts "All finished"
    }
}

# A signal handler that gets called when a child process finished.
# This handler needs to exit quickly, so it delegates the real works to
# the proc updatePidList
proc childTerminated {} {
    # Restart the handler
    signal -restart trap SIGCHLD childTerminated

    # Update the list of process IDs
    while {![catch {wait -nohang} stat] && $stat ne {}} {
        after idle [list updatePidList $stat]
    }
}

#
# Main starts here
#

puts "Main begins"
set NUMBER_OF_PROCESSES_TO_LAUNCH 10
set pidList {}
set allFinished 0

# When a child process exits, call proc childTerminated
signal -restart trap SIGCHLD childTerminated

# Spawn many processes
for {set i 0} {$i < $NUMBER_OF_PROCESSES_TO_LAUNCH} {incr i} {
    set childId [exec tclsh child.tcl $i &]
    puts "child #$i, pid=$childId"
    lappend pidList $childId
    after 1000
}

# Do some processing
puts "list of processes: $pidList"
puts "Waiting for child processes to finish"
# Do some more processing if required

# After all done, wait for all to finish before exiting
vwait allFinished

puts "Main ends"

child.tcl

孩子.tcl

#!/usr/bin/env tclsh
# child script: simulate some lengthy operations

proc randomInteger {min max} {
    return [expr int(rand() * ($max - $min + 1) * 1000 + $min)]
}

set duration [randomInteger 10 30]
puts "  child #$argv runs for $duration miliseconds"
after $duration
puts "  child #$argv ends"

Sample output for running main.tcl

运行 main.tcl 的示例输出

Main begins
child #0, pid=64525
  child #0 runs for 17466 miliseconds
child #1, pid=64526
  child #1 runs for 14181 miliseconds
child #2, pid=64527
  child #2 runs for 10856 miliseconds
child #3, pid=64528
  child #3 runs for 7464 miliseconds
child #4, pid=64529
  child #4 runs for 4034 miliseconds
child #5, pid=64531
  child #5 runs for 1068 miliseconds
child #6, pid=64532
  child #6 runs for 18571 miliseconds
  child #5 ends
child #7, pid=64534
  child #7 runs for 15374 miliseconds
child #8, pid=64535
  child #8 runs for 11996 miliseconds
  child #4 ends
child #9, pid=64536
  child #9 runs for 8694 miliseconds
list of processes: 64525 64526 64527 64528 64529 64531 64532 64534 64535 64536
Waiting for child processes to finish
Waiting for 8 processes
Waiting for 8 processes
  child #3 ends
Waiting for 7 processes
  child #2 ends
Waiting for 6 processes
  child #1 ends
Waiting for 5 processes
  child #0 ends
Waiting for 4 processes
  child #9 ends
Waiting for 3 processes
  child #8 ends
Waiting for 2 processes
  child #7 ends
Waiting for 1 processes
  child #6 ends
All finished
Main ends