并行进程:将输出附加到 bash 脚本中的数组

BiB*_*iBi 4 linux bash shell-script

我有一个 for 循环,其中task调用了一个函数。每次调用该函数都会返回一个附加到数组的字符串。我想并行化这个 for 循环。我尝试使用&但它似乎不起作用。

这是未并行化的代码。

task (){ sleep 1;echo "hello $1"; }
arr=()

for i in {1..3}; do
    arr+=("$(task $i)")
done

for i in "${arr[@]}"; do
    echo "$i x";
done
Run Code Online (Sandbox Code Playgroud)

输出是:

hello 1 x
hello 2 x
hello 3 x
Run Code Online (Sandbox Code Playgroud)

伟大的!但是现在,当我尝试将它与

[...]
for i in {1..3}; do
    arr+=("$(task $i)")&
done
wait
[...]
Run Code Online (Sandbox Code Playgroud)

输出为空。

更新 #1

关于task功能:

  • 该函数task需要一些时间来运行,然后输出一个字符串。收集完所有字符串后,另一个 for 循环将遍历字符串并执行其他一些任务。
  • 顺序无关紧要。输出字符串可以由单行字符串组成,可能包含多个由空格分隔的单词。

ilk*_*chu 5

您不能向后台发送赋值,因为后台进程是 shell 的一个分支,并且在主 shell 中看不到对变量的更改。

但是您可以并行运行一堆任务,将它们全部输出到管道,然后读取输出的任何内容。或者实际上,使用进程替换,以避免在子shell中执行管道中的命令问题(请参阅为什么我的变量在一个“while read”循环中是局部的,而不是在另一个看似相似的循环中?

只要输出是以原子方式编写的单行,它们就不会混合,但可能会重新排序:

$ task() { sleep 1; echo "$1"; }
$ time while read -r line; do arr+=("$line"); done < <(for x in 1 2 3 ; do task "$x" & done)
real    0m1.006s
$ declare -p arr
declare -a arr=([0]="2" [1]="1" [2]="3")
Run Code Online (Sandbox Code Playgroud)

以上将同时运行所有任务。还有GNU 并行-P在 GNU xargs 中),它专门用于并行运行任务,并且只能同时运行几个。Parallel 还会缓冲任务的输出,因此您不会获得混合数据,即使任务分部分写入行也是如此。

$ mapfile -t arr < <(parallel -j4 bash ./task.sh ::: {a,b,c})
$ declare -p arr
declare -a arr=([0]="a" [1]="b" [2]="c")
Run Code Online (Sandbox Code Playgroud)

mapfile这里Bash将输入行读入数组,类似于while read .. arr+=()上面的循环。)

如上所述运行外部脚本很简单,但实际上您也可以让它运行导出的函数,当然所有任务都在 shell 的独立副本中运行,因此它们将拥有自己的每个变量副本等。

$ export -f task
$ mapfile -t arr < <(parallel task ::: {a,b,c})
Run Code Online (Sandbox Code Playgroud)

上面的例子碰巧保持a, b, 和c有序,但这是一个巧合。使用parallel -k它确保输出保持有序。