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
이러한 셸의 변수를 업데이트합니다. 실행하는 쉘 중 하나입니다 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
배열의 요소를 올바른 순서로 가져올 수 있습니다.
답변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
.