1

I'm working with this:

GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)

I have a script like below:

#!/bin/bash

map2=()
result=""
f() {
    tmpA=(12 34 7844);
    map2=("${tmpA[@]}");
    echo true;
    return;
}

result=$(f)
echo result=$result : array=${map2[@]}

Which returns:

result=true : array=

if I replace result=$(f) simply by f it returns:

result= : array=12 34 7844

I could not find the way to modify the global array but also get the return value. Any idea on how to achieve this?

Dte
  • 239
  • 1
  • 3
  • 8
  • 1
    See [Access variables set inside command substitutions](https://unix.stackexchange.com/q/340063/264812). – pjh Feb 18 '23 at 14:22

2 Answers2

3

Any environment changes made inside $( ... ) are lost when it exits.

However, bash allows a way of changing arguments passed "by reference" by using the declare -n command:

#!/bin/bash

map2=()
result=""
f() {
    declare -n resultRef=$1
    declare -n map2Ref=$2

    local tmpA=(12 34 7844)
    map2Ref=("${tmpA[@]}")
    map2Ref[4]=999
    resultRef=true

    echo during: resultRef=$resultRef : arrayRef=${map2Ref[*]} : tmpA=${tmpA[*]}
}

f result map2

echo after: resultRef=$resultRef : arrayRef=${map2Ref[*]} : tmpA=${tmpA[*]}
echo result=$result : array=${map2[*]}

Variables declared in this way behave like local - they are discarded when the function returns.

during: resultRef=true : arrayRef=12 34 7844 999 : tmpA=12 34 7844
after: resultRef= : arrayRef= : tmpA=
result=true : array=12 34 7844 999
jhnc
  • 11,310
  • 1
  • 9
  • 26
  • +1. I was not aware this is possible: `f() { declare -n b=$1; b=100; }; a=2; f a; echo "$a"` (and you get 100). – Walter A Feb 18 '23 at 16:21
  • @WalterA Unfortunately the implementation is not very robust. If one needs to pass complicated data structures around, switching to a "proper" language may be sensible. – jhnc Feb 18 '23 at 16:55
3

Using command substitution (i.e., the $(f) construct), you are creating a subshell. Subshells inherit variables from their parent shells, but a subshell cannot modify its parent shell environment.

If you just call your function and check its exit code $? then you should be able to generate your desired output:

#!/bin/bash

map2=()
result="false"
my_func() {
    tmpA=(12 34 7844)
    map2=("${tmpA[@]}")
}

if my_func ; then
    result="true"
fi

printf "result=%s : array=%s\n" "$result" "${map2[@]}"

Output:

result=true : array=12 34 7844

Note that you can use bash -x yourscript to enable xtrace for more debugging output and you can paste your script into https://www.shellcheck.net/ for help as well.

j_b
  • 1,975
  • 3
  • 8
  • 14