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
あなたのスクリプトは、コマンドを並列に実行するスクリプトであるparallel
GNUスクリプトのようです。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
要素を正しい順序で取得できます。