並列処理を使用して配列に追加することはできません。

並列処理を使用して配列に追加することはできません。

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")

最初の例はループの機能的スタイルを翻訳したようですが、何が起こっているのでしょうか。

ベストアンサー1

あなたのスクリプトは、コマンドを並列に実行するスクリプトであるparallelGNUスクリプトのようです。perl

渡されたコマンドがそのシェルによって解釈されるようにどのシェルから呼び出されたかを確認することは非常に困難ですが、このために別のプロセスでそのシェルの新しい呼び出しを実行します。

実行する場合:

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を使用すると、シェルが実行するアクションが表示されます。

各bashシェルが独自のbashシェルに要素を追加して$arrから終了し、もちろん独自のメモリスペース(それぞれの$arr配列を含む)が消えることがわかります。この配列は、システムのすべてのシェル呼び出し中に自動的に共有されるわけではありませ$arrん。bash

とにかく、コマンドを同時に実行するとは、そのコマンドを別のプロセスで実行することを意味するため、呼び出しシェルでこれらの機能を実行することはできません。これらの機能は別々のプロセスの新しいシェルインスタンスで実行され、arrこれらのシェルの変数を更新します。実行しているシェルの1つですparallel

bashにはマルチスレッドサポートが組み込まれていないため、parallelコマンドがシェル内にある場合、またはシェル関数で実装されている場合でも、それぞれ独自のメモリを持つ別々のプロセスでコマンドを実行する必要があります。以下で見つけることができます:

append 1 & append 2 & append 3 & wait

または:

append 1 | append 2 | append 3

$arr親シェルの配列も変更されません。

並列に開始された各タスクの結果を収集したい場合は、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推測を避けるためにどのシェルを使うべきかを教えてくれます。)

各シェルの出力を一時ファイルに保存して標準出力に順次ダンプすると、配列のparallel要素を正しい順序で取得できます。

おすすめ記事