我还有另一个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
进程替换是否会更有效,特别是如果您不需要保存文件OutputA
和OutputB
,并且只关心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-9
10 个文件和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 并等待。