bash:等待进程替换子 shell 完成

Rus*_*mur 2 bash xargs process-substitution

bash 如何等待进程替换中使用的子 shell 在以下构造中完成?(这当然是从我正在使用的真实 for 循环和子 shell 简化而来的,但它很好地说明了意图。)

for i in {1..3}; do
    echo "$i"
done > >(xargs -n1 bash -c 'sleep 1; echo "Subshell: $0"')
echo "Finished"
Run Code Online (Sandbox Code Playgroud)

印刷:

Finished
Subshell: 1
Subshell: 2
Subshell: 3
Run Code Online (Sandbox Code Playgroud)

代替:

Subshell: 1
Subshell: 2
Subshell: 3
Finished
Run Code Online (Sandbox Code Playgroud)

如何让 bash 等待这些子 shell 完成?

更新

使用进程替换的原因是我想使用文件描述符来控制打印到屏幕的内容以及发送到进程的内容。这是我正在做的事情的完整版本:

for myFile in file1 file2 file3; do
    echo "Downloading $myFile"     # Should print to terminal
    scp -q $user@$host:$myFile ./  # Might take a long time
    echo "$myFile" >&3             # Should go to process substitution
done 3> >(xargs -n1 bash -c 'sleep 1; echo "Processing: $0"')
echo "Finished"
Run Code Online (Sandbox Code Playgroud)

印刷:

Downloading file1
Downloading file2
Downloading file3
Finished
Processing: file1
Processing: file2
Processing: file3
Run Code Online (Sandbox Code Playgroud)

处理每个数据可能比传输花费更长的时间。文件传输应该是连续的,因为带宽是限制因素。我想在收到每个文件后开始处理它,而不是等待所有文件都传输完毕。处理可以并行完成,但仅限于有限数量的实例(由于内存/CPU 有限)。因此,如果第五个文件刚刚完成传输,但只有第二个文件完成处理,则第三个和第四个文件应该在第五个文件处理之前完成处理。同时第六个文件应该开始传输。

Cha*_*ffy 7

Bash 4.4 允许您收集用 替换的进程的 PID $!,因此您实际上可以使用wait,就像使用后台进程一样:

case $BASH_VERSION in ''|[123].*|4.[0123])
  echo "ERROR: Bash 4.4 required" >&2; exit 1;;
esac

# open the process substitution
exec {ps_out_fd}> >(xargs -n1 bash -c 'sleep 1; echo "Subshell: $0"'); ps_out_pid=$!

for i in {1..3}; do
  echo "$i"
done >&$ps_out_fd

# close the process substitution
exec {ps_out_fd}>&-

# ...and wait for it to exit.
wait "$ps_out_pid"
Run Code Online (Sandbox Code Playgroud)

除此之外,考虑flock风格锁定——但要注意竞争:

for i in {1..3}; do
  echo "$i"
done > >(flock -x my.lock xargs -n1 bash -c 'sleep 1; echo "Subshell: $0"')

# this is only safe if the "for" loop can't exit without the process substitution reading
# something (and thus signalling that it successfully started up)

flock -x my.lock echo "Lock grabbed; the subshell has finished"
Run Code Online (Sandbox Code Playgroud)

也就是说,考虑到您的实际用例,您想要的应该看起来更像是:

download() {
  for arg; do
    scp -q $user@$host:$myFile ./ || (( retval |= $? ))
  done
  exit "$retval"
}
export -f download

printf '%s\0' file1 file2 file3 |
  xargs -0 -P2 -n1 bash -c 'download "$@"' _
Run Code Online (Sandbox Code Playgroud)