Bash,如何让某些后台进程运行但等待其他进程?

Bash,如何让某些后台进程运行但等待其他进程?

我还有另一个wait控制 &&&问题。

假设我有一个类似这样的脚本,我想同时做尽可能多的工作:

# may take some hours
something InputA > IrrelevantA &
something InputB > IrrelevantB &

# may take an hour
(
   somethingElse InputA > OutputA &
   somethingElse InputB > OutputB &
)&& combine OutputA OutputB > Result

...morestuff

问题1:在脚本中,是否combine等待两个somethingElse进程完成,同时两个something进程继续?

问题 2:如果不是——我怀疑不是——我如何才能combine只等待这两个somethingElse进程,而something上面的进程继续在后台运行?

答案1

在您的示例中,该combine命令将在子 shell 退出后立即运行(并且假设最后一个后台进程启动时没有错误)。由于没有wait命令,子 shell 将在作业启动后立即退出。

如果您想根据两个或多个同时后台进程的返回值执行命令,那么除了使用临时文件作为返回值之外,我看不到任何其他方法。这是因为wait只能返回的返回值它等待的进程的数量。此外,由于后台进程必须在子 shell 中运行才能获取其返回值,因此它们不能存储在变量中。你可以这样做:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

tmp1=$(mktemp)
tmp2=$(mktemp)

( somethingElse InputA >OutputA; echo $? >"$tmp1" ) &
proc1=$!

( somethingElse InputB >OutputB; echo $? >"$tmp2" ) &
proc2=$!

wait "$proc1" "$proc2"

read ret1 <"$tmp1"
read ret2 <"$tmp2"
[ "$ret1" = 0 && "ret2" = 0 ] && combine OutputA OutputB >Result

rm "$tmp1" "$tmp2"

如果您并不真正关心返回值,您可以正常启动作业并使用wait

something InputA >IrrelevantA &
something InputB >IrrelevantB &

somethingElse InputA >OutputA &
proc1=$!

somethingElse InputB >OutputB &
proc2=$!

wait "$proc1" "$proc2"
combine OutputA OutputB >Result

答案2

您可以使用wait命令:

(echo starting & sleep 10 & wait) && echo done

您可以看到“开始”行立即发生,“完成”行等待 10 秒。

答案3

进程替换是否会更有效,特别是如果您不需要保存文件OutputAOutputB,并且只关心Result?这是否会特别节省时间,因为如果写入磁盘、保存文件时的 I/O 速度很慢OutputA,并且OutputB可能是速率限制步骤?

combine  <(somethingElse InputA)  <(somethingElse InputB)  >  Result

进程替换允许您将命令放入其中,<(..here..)而不是将输出保存到文件中,然后从中读取作为“组合”步骤中的输入。

如果内存是一个限制,并且内存的大小outputA超过outputB了内存的容量,那么它会破坏整个目的吗?

combine等到两个进程都完成后再开始运行吗?

答案4

我实际上在另一个答案中准确地演示了如何完成此类事情这里。这个答案是关于确保后台进程维护 2 个日志的问题,所以我用 10 个日志进行了演示。

演示脚本

cat <<-\DEMO >|${s=/tmp/script} 
printf 'tty is %s\nparent pid is %s\npid is pid=%s\n' \
     "$(tty)" "$PPID" "$$"
exec 1>&2 ; nums=$(seq 0 9)
rm ${files=$(printf "/tmp/file%s\n" $nums)}
for n in $nums ; do { for f in $files ; do
    echo "Line $n" >>"$f" ; done
sleep 1 ; } ; done
#END
DEMO

运行演示

s=/tmp/script ;chmod +x $s ;info="$(($s &)2>&- &)"
echo "$info" ; pid="${info##*=}" ; echo
while ps -p $pid >/dev/null ; do sleep 3 ; done
for f in /tmp/file[0-9] ; do
    printf 'path : %s\tline count : %s\n' \
        $f $(<$f wc -l)
done

输出:

tty is not a tty
parent pid is 1
pid is 12123

path : /tmp/file0    line count : 10
path : /tmp/file1    line count : 10
path : /tmp/file2    line count : 10
path : /tmp/file3    line count : 10
path : /tmp/file4    line count : 10
path : /tmp/file5    line count : 10
path : /tmp/file6    line count : 10
path : /tmp/file7    line count : 10
path : /tmp/file8    line count : 10
path : /tmp/file9    line count : 10

上面证明了。它构建并运行一个名为/tmp/script, chmod它作为可执行文件,并在&background的一个&backgrounded ( subshell )

剧本rms /tmp/file0-910 个文件和echoes每秒一行进入所有 10 个人。我捕捉到一些$info来自否认的流程并通过以下方式呈现$(command substitution). While ps仍在报道$pid我捕获,我知道它仍在运行,所以我sleep.完成后,所有 10 个文件中的行都将被计数wc.

以这种方式调用进程后,您可以自由地关闭其原始父进程,并且它将继续运行 - 它实际上已被否认。这也意味着您不能使用传统的wait命令,但等待ps无论如何,回报应该更加稳健。

我认为值得一提的是,该进程实际上最初是在$(command substitution)printfs我的$info我希望这样我可以有效地控制它。但是一旦它下降终端输出exec 1>&2(在与 相同的子 shell 中关闭2>&-),该进程逃脱了,我必须在另一端等待它。有点两全其美,特别是当您使用它来处理输入管道时,只要您能够将注意力集中在所有重定向和进程领导者上。

其他一切都只是为了在这里演示。运行它所需要的只是顶部脚本和:

info="$(($script_path &)2>&- &)"    

笔记:这只会将我想要演示的内容精确地打印到终端。正如所指出的$PPID,该进程被终端拒绝,并且是该进程的直接子进程$PID 1.

如果您想同时运行其中两个并等待它们,您可以手动ps他们的 pid 并等待。

相关内容