无法使用并行附加到数组

无法使用并行附加到数组

当我使用 时,我无法追加到数组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.

相关内容