当我使用 时,我无法追加到数组parallel
,使用 for 循环没有问题。
并行示例:
append() { arr+=("$1"); }
export -f append
parallel -j 0 append ::: {1..4}
declare -p arr
输出:
-bash: declare: arr: not found
对于循环:
for i in {1..4}; do arr+=("$i"); done
declare -p arr
输出:
declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4")
我认为第一个示例是函数式风格的 for 循环的翻译,那么这是怎么回事呢?
答案1
您的parallel
似乎是 GNU 脚本,它是一个perl
并行运行命令的脚本。
它非常努力地判断从哪个 shell 调用它,以便您传递给它的命令由该 shell 解释,但要做到这一点,它会在单独的进程中运行该 shell 的新调用。
如果你运行:
bash-5.2$ env SHELLOPTS=xtrace PS4='bash-$$> ' strace -qqfe /exec,/exit -e signal=none parallel -j 0 append ::: {1..4}
execve("/usr/bin/parallel", ["parallel", "-j", "0", "append", ":::", "1", "2", "3", "4"], 0x7ffe5e848c90 /* 56 vars */) = 0
[...skipping several commands run by parallel during initialisation...]
[pid 7567] execve("/usr/bin/bash", ["/usr/bin/bash", "-c", "append 1"], 0x55a2615f03e0 /* 67 vars */) = 0
bash-7567> append 1
bash-7567> arr+=("$1")
[pid 7567] exit_group(0) = ?
[pid 7568] execve("/usr/bin/bash", ["/usr/bin/bash", "-c", "append 2"], 0x55a2615f03e0 /* 67 vars */) = 0
[pid 7568] exit_group(0) = ?
[pid 7569] execve("/usr/bin/bash", ["/usr/bin/bash", "-c", "append 3"], 0x55a2615f03e0 /* 67 vars */) = 0
bash-7568> append 2
bash-7568> arr+=("$1")
[pid 7569] exit_group(0) = ?
[pid 7570] execve("/usr/bin/bash", ["/usr/bin/bash", "-c", "append 4"], 0x55a2615f03e0 /* 67 vars */) = 0
bash-7569> append 3
bash-7569> arr+=("$1")
[pid 7570] exit_group(0) = ?
bash-7570> append 4
bash-7570> arr+=("$1")
exit_group(0) = ?
其中strace
显示哪些命令由哪个进程执行,该xtrace
选项使 shell 显示它的作用。
您将看到每个 bash shell 向其自己的 bash shell 附加一个元素$arr
,然后退出,当然,它们自己的内存空间(包括其各自的$arr
数组)消失了,该$arr
数组不会bash
在系统上的所有 shell 调用之间自动共享。
在任何情况下,同时运行命令意味着在不同的进程中运行它们,因此它无法在调用 shell 中运行这些函数,这些函数将在单独进程的新 shell 实例中运行,并且它们将更新arr
这些 shell 的变量,不是您运行的 shell 之一parallel
。
鉴于 bash 没有内置的多线程支持,即使parallel
是 shell 的内部命令或作为 shell 函数实现,它仍然需要在单独的进程中运行命令,每个进程都有自己的内存。您会在以下位置找到它:
append 1 & append 2 & append 3 & wait
或者:
append 1 | append 2 | append 3
$arr
父 shell 的数组也没有被修改。
如果您想收集并行启动的每个作业的结果,可以通过 stdout 或通过文件来完成。
例如:
#! /bin/bash -
do_something() {
output=$(
echo "$1: some complex computation or otherwise there would
be no point using GNU parallel and its big overhead"
)
# output the result NUL delimited.
printf '%s\0' "$output"
}
export -f do_something
readarray -td '' arr < <(
PARALLEL_SHELL=/bin/bash parallel do_something ::: {1..4}
)
typeset -p arr
(这里告诉parallel
我们使用哪个 shell 以避免猜测)。
请注意,parallel
将每个 shell 的输出存储在临时文件中,并按顺序将它们转储到标准输出上,以便您以正确的顺序获取数组的元素。
答案2
您应该知道parset
它是 GNU Parallel 的一部分:
$ parset arr echo ::: 1 2 3 4
$ declare -p arr
declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4")
它不会附加(因此它不是您问题的有效答案),但它可能对您仍然有用。
答案3
这有效:
arr+=($(parallel -j 0 echo ::: {1..4}))
该数组丢失了,因为它是在并行实例中设置的,与echo
.