병렬 프로세스: bash 스크립트의 배열에 출력 추가

병렬 프로세스: bash 스크립트의 배열에 출력 추가

task함수가 호출되는 for 루프가 있습니다 . 이 함수를 호출할 때마다 배열에 추가된 문자열이 반환됩니다. 나는 이 for 루프를 병렬화하고 싶습니다. 사용해 보았지만 &작동하지 않는 것 같습니다.

이것은 비교할 수 없는 코드입니다.

task (){ sleep 1;echo "hello $1"; }
arr=()

for i in {1..3}; do
    arr+=("$(task $i)")
done

for i in "${arr[@]}"; do
    echo "$i x";
done

출력은 다음과 같습니다

hello 1 x
hello 2 x
hello 3 x

엄청난! 하지만 지금은 그것을 결합하려고 할 때

[...]
for i in {1..3}; do
    arr+=("$(task $i)")&
done
wait
[...]

출력이 비어 있습니다.

업데이트 #1

기능 정보 task:

  • 이 함수는 task실행하는 데 약간의 시간이 걸리고 문자열을 출력합니다. 모든 문자열이 수집된 후 또 다른 for 루프는 문자열을 반복하고 다른 작업을 수행합니다.
  • 순서는 중요하지 않습니다. 출력 문자열은 공백으로 구분된 여러 단어를 포함하는 단일 행 문자열로 구성될 수 있습니다.

답변1

백그라운드 프로세스는 셸의 분기이고 변수 변경 사항은 기본 셸에 표시되지 않기 때문에 할당을 백그라운드로 보낼 수 없습니다.

그러나 여러 작업을 병렬로 실행하고 모두 파이프로 출력하고 출력되는 내용을 읽을 수 있습니다. 아니면 실제로 사용하세요.프로세스 교체서브셸의 파이프에서 명령을 실행할 때 발생하는 문제를 방지하려면(참조내 변수가 하나의 "읽는 동안" 루프에서는 로컬이지만 겉보기에 유사한 다른 루프에서는 로컬이 아닌 이유는 무엇입니까?)

출력이 원자적으로 작성된 한 줄이면 혼합되지 않지만 재정렬될 수 있습니다.

$ task() { sleep 1; echo "$1"; }
$ time while read -r line; do arr+=("$line"); done < <(for x in 1 2 3 ; do task "$x" & done)
real    0m1.006s
$ declare -p arr
declare -a arr=([0]="2" [1]="1" [2]="3")

위의 작업은 모든 작업을 동시에 실행합니다. 게다가GNU 병렬( -PGNU xargs에서)는 작업을 병렬로 실행하도록 설계되었으며 동시에 몇 가지 작업만 실행합니다. 또한 병렬 처리는 작업 출력을 버퍼링하므로 작업이 행을 조각으로 작성하더라도 혼합된 데이터를 얻지 못합니다.

$ mapfile -t arr < <(parallel -j4 bash ./task.sh ::: {a,b,c})
$ declare -p arr
declare -a arr=([0]="a" [1]="b" [2]="c")

(Bash는 위의 루프 mapfile와 유사하게 여기에서 입력 라인을 배열로 읽습니다 .)while read .. arr+=()

위와 같이 외부 스크립트를 실행하는 것은 간단하지만 실제로 내보낸 함수도 실행하도록 할 수 있습니다. 물론 모든 작업은 별도의 셸 복사본에서 실행되므로 모든 변수 등의 자체 복사본을 갖게 됩니다.

$ export -f task
$ mapfile -t arr < <(parallel task ::: {a,b,c})

위의 예는 a, b, 의 순서를 유지하는 경우가 많지만 c이는 우연의 일치입니다. parallel -k출력이 순서대로 유지되도록 하려면 이를 사용하십시오 .

답변2

Bourne과 같은 쉘에서 이식 가능한 약간 순진하지만 강력한 접근 방식입니다.

#!/bin/sh

task () {
    tid="$1"
    printf 'tid %d: Running...\n' "$tid"
    sleep "$(( RANDOM % 5 ))"
    printf 'tid %d: Done.\n' "$tid"
}

ntasks=10

tid=0
while [ "$tid" -ne "$ntasks" ]; do
    tid=$(( tid + 1 ))
    printf 'main: Starting task with tid=%d\n' "$tid"
    task "$tid" >"output.$tid" 2>&1 &
done

wait

tid=0
while [ "$tid" -ne "$ntasks" ]; do
    tid=$(( tid + 1 ))
    printf 'main: Processing output from task with tid=%d\n' "$tid"
    # do something with "output.$tid"
done

이는 첫 번째 루프에서 작업을 생성하고 두 번째 루프에서 출력을 처리하기 전에 작업이 완료될 때까지 기다립니다. 이는 작업이 많은 양의 데이터를 생성하는 경우 적합합니다.

실행 중인 작업 수를 최대 4개로 제한하려면 초기 루프를 다음과 같이 변경할 수 있습니다.

tid=0
while [ "$tid" -ne "$ntasks" ]; do
    tid=$(( tid + 1 ))
    printf 'main: Starting task with tid=%d\n' "$tid"
    task "$tid" >"output.$tid" 2>&1 &

    if [ "$(( tid % 4 ))" -eq 0 ]; then
        wait
    fi
done

답변3

parset당신은 (20170422 이후 GNU Parallel의 일부) 또는 env_parset(20171222부터 사용 가능) 을 찾고 있습니다 :

# If you have not run:
#    env_parallel --install
# and logged in again, then you can instead run this to activate (env_)parset:
. `which env_parallel.bash`

task (){
  echo "hello $1"
  sleep 1.$1
  perl -e 'print "binary\001\002\n"'
  sleep 1.$1
  echo output of parallel jobs do not mix
}
env_parset arr task ::: {1..3}
env_parset a,b,c task ::: {1..3}

echo "${arr[1]}" | xxd
echo "$b" | xxd

parsetBash/Ksh/Zsh(어레이 포함), ash/dash(어레이 제외) 지원.

관련 정보