Bash 패스 연관 배열을 배경 함수로/부터 전달

Bash 패스 연관 배열을 배경 함수로/부터 전달

bash 연관 배열을 참조로 함수에 전달한 다음 함수가 완료된 후 기본 스크립트에서 변경된 내용을 확인할 수 있도록 노력하고 있습니다. 가장 직접적인 방법인 것 같은 것을 찾았습니다여기내 경우를 제외하고는 기능이 실행 중입니다.백그라운드에서. 내가 무엇을 하든 이 경우에는 위에 링크된 솔루션이 작동하지 않는 것 같습니다.

아래 코드 조각에서는 위의 링크에서 작동하는 예제 코드를 가져와 함수 호출에 "&"를 추가하고 다음 줄에 "wait"를 추가하여 문제를 가능한 한 간단하게 설명했습니다.

Bash가 기본 스크립트와 백그라운드 기능이 서로 밟히는 것을 방지하려고 시도하는 것 같지만 이를 해결하는 방법을 모르겠습니다.

샘플 코드:

foo () {
    declare -n fooarray="$1"
    fooarray["fookey"]=foovalue
}

declare -A myarray

myarray["mainkey"]=mainvalue
foo myarray &
wait

for key in "${!myarray[@]}"; do
    printf '%s = %s\n' "$key" "${myarray[$key]}"
done

산출:

bash-4.4$ ./test.sh
mainkey = mainvalue

어떤 도움이라도 대단히 감사하겠습니다. 나는 배열의 내용을 파일에 쓴 다음 다시 구문 분석하는 것과 같은 어리석은 일을 할 것이라는 것을 알고 있지만 이보다 더 우아한 솔루션을 기대합니다.

답변1

핵심요약: 백그라운드에서 실행되는 기능은 별도의 프로세스입니다. 백그라운드에서 실행되는 동안 하위 항목은 데이터 사본을 받습니다. 이 복사본에 대한 변경 사항은 상위 복사본에 전파되지 않습니다.


코드에 줄 번호를 추가해 보겠습니다.

     1  foo () {
     2      declare -n fooarray="$1"
     3      fooarray["fookey"]=foovalue
     4  }
     5  
     6  declare -A myarray
     7  
     8  myarray["mainkey"]=mainvalue
     9  foo myarray &
    10  wait
    11  
    12  for key in "${!myarray[@]}"; do
    13      printf '%s = %s\n' "$key" "${myarray[$key]}"
    14  done

9행에서 bash는 자신을 두 개의 프로세스로 분기합니다. 하나는 계속 실행되고 10행에서 중지되는 상위 프로세스입니다. wait이 프로세스는 9행 이전에 존재했던 데이터 복사본을 사용하여 foo 함수의 내용을 실행합니다.

관찰: 상위 프로세스는 function 의 코드를 실행하지 않는 foo반면 하위 프로세스는 라인 2와 3만 실행합니다.

하위 항목에서 2행은 다른 연결된 답변에 설명된 대로 배열 참조 조회를 수행합니다. fooarray["mainkey"]언급된 복사 작업으로 인해 하위 항목의 값은 "mainvalue"입니다. 그런 다음 3번째 줄에 "fookey"를 추가하지만 이 작업을 수행합니다.사본자료. 3행 이후에는 3행의 할당이 성공했기 때문에 하위 프로세스가 성공적으로 종료됩니다.

부모는 실행되지 않고 foo자식은 복사본을 수정하므로 부모는 "fookey"변경 사항을 볼 수 없습니다.

하위 항목이 상위 항목을 수정하도록 하려면 지속적인 프로세스 간 통신 메커니즘(파일이나 파이프와 같은 "IPC")을 사용해야 하며 해당 IPC를 읽고 업데이트하여 상위 항목이 데이터를 "선택"하도록 해야 합니다. 복사.

답변2

가장 우아하지 않을 수도 있고 질문에 정확히 대답하지도 않지만(이것은 배열을 내부에서 수정하지 않음) 일반 반환(ish)처럼 백그라운드 함수에서 수정된 배열을 출력할 수 있습니다. 출력을 캡처하는 대신 프로세스를 사용하고 업데이트를 직접 복사하십시오.

 1 #!/bin/bash
 2   
 3 foo () {
 4   declare -n fooarray="$1"
 5   fooarray["fookey"]=foovalue
 6   echo "${fooarray[@]@K}"
 7 } 
 8   
 9 declare -A myarray
10 myarray["mainkey"]=mainvalue
11  
12 # Execute "foo myarray" asynchronously using [process substitution][1]
13 # No need to run it in the background (I.e. using "&")
14 exec 3< <(foo myarray)
15 p=$!
16 wait $p
17  
18 echo "<pre> ${myarray[*]@A}"
19  
20 # Capture the return into an array
21 # Bash will apply word splitting here
22 ret=($(cat <&3))
23 echo "<ret> ${ret[*]@A}"
24  
25 # Copy "ret" into "myarray"
26 # Remove auto escaping bash does when using @K
27 for (( i=1; i<${#ret[@]}; i+=2 )); do myarray[${ret[$i-1]}]=${ret[$i]//\"/}; done
28 echo "<post> ${myarray[*]@A}"
29  
30 for key in "${!myarray[@]}"; do
31   printf '%s = %s\n' "$key" "${myarray[$key]}"
32 done

산출:

<pre> declare -A myarray=([mainkey]="mainvalue" )
<ret> declare -a ret=([0]="fookey" [1]="\"foovalue\"" [2]="mainkey" [3]="\"mainvalue\"")
<post> declare -A myarray=([fookey]="foovalue" [mainkey]="mainvalue" )
fookey = foovalue
mainkey = mainvalue

프로세스 대체 참고

고쳐 쓰다

의견에서 언급했듯이 여기에는 주의 사항이 있습니다. 이 솔루션은 연관 배열의 각 값에 단어 값이 있다고 가정합니다. 예를 들어, "foovalue"가 "foo value"인 경우 Bash의 단어 분리는 모든 종류의 혼란을 초래할 것입니다:

출력(라인 5에서 foovalue가 "foo value"로 설정된 경우):

<pre> declare -A myarray=([mainkey]="mainvalue" )
<ret> declare -a ret=([0]="fookey" [1]="\"foo" [2]="value\"" [3]="mainkey" [4]="\"mainvalue\"")
<post> declare -A myarray=([fookey]="foo" [mainkey]="mainvalue" ["value\""]="mainkey" )
fookey = foo 
mainkey = mainvalue
value" = mainkey

이 프로세스 리디렉션 방법을 계속 사용하면서 이 상황을 처리하려면 다음 두 가지 접근 방식을 생각해 볼 수 있습니다.

  1. 확장(@A)의 할당 형식을 반환한 다음 eval을 사용하여 생성합니다.
  2. 반환된 문자열에서 각 변수의 시작과 끝을 적절하게 캡처하려면 루프 전에 고급 구문 분석을 적용하세요.

다음은 위의 (1)의 예입니다.

 1 #!/bin/bash
 2
 3 foo () {
 4   declare -n fooarray="$1"
 5   fooarray["fookey"]="foo value"
 6   echo "${fooarray[@]@A}"
 7 }
 8
 9 declare -A myarray
10 myarray["mainkey"]=mainvalue
11
12 # Execute "foo myarray" asynchronously using [process substitution][1]
13 # No need to run it in the background (I.e. using "&")
14 exec 3< <(foo myarray)
15 wait $!
16
17 echo "<pre> ${myarray[*]@A}"
18
19 # Capture the return into a string
20 ret="$(cat <&3)"
21 echo "<ret> ${ret[*]}"
22
23 # Note the returned array is named "myarray"
24 # If you want to overwrite "myarray" with the return entirely,
25 # you can call eval on it directly, otherwise, swap the name
26 ret="${ret[*]/myarray/fooarray}"
27 echo "<rv2> ${ret[*]}"
28
29 # Eval ret into existence
30 eval "${ret[*]}"
31
32 # Copy "fooarray" into "myarray"
33 # Quote to prevent globing or word splitting
34 for key in "${!fooarray[@]}"; do myarray[$key]="${fooarray[$key]}"; done
35 echo "<post> ${myarray[*]@A}"
36
37 for key in "${!myarray[@]}"; do
38   printf '%s = %s\n' "$key" "${myarray[$key]}"
39 done

산출:

<pre> declare -A myarray=([mainkey]="mainvalue" )
<ret> declare -A myarray=([fookey]="foo value" [mainkey]="mainvalue" )
<rv2> declare -A fooarray=([fookey]="foo value" [mainkey]="mainvalue" )
<post> declare -A myarray=([fookey]="foo value" [mainkey]="mainvalue" )
fookey = foo value
mainkey = mainvalue

관련 정보