1

I want to fill an associative array in bash in somewhat non-trivial setup. I have a pipeline of commands to produce the required input for the array.

Here is a minimal/toy example:

declare -A mapping
seq 10 | while read i; do
    key="key_$i"
    val="val_$i"
    echo "mapping[$key]=$val"
    mapping["${key}"]="${val}"
done

echo "${mapping["key_1"]}"
echo "${mapping["key_2"]}"

In this example mapping is changed inside while, but these changes do not propagate into the global namespace. I think this is because while works inside a separate subshell, thus namespaces have diverged.

In order to avoid (what I suggest) the problem with subshells, I came up with the following:

declare -A mapping
while read i; do
    key="key_$i"
    val="val_$i"
    echo "mapping[$key]=$val"
    mapping["${key}"]="${val}"
done < <(seq 10)

echo "${mapping["key_1"]}"
echo "${mapping["key_2"]}"

Thus, the generation part explicitly goes into a subshell, while the while loop left at the top-level alone. The construction works.

My questions are: is there any better way to accomplish my goal? And, is my suggestion about subshells correct? If so, why bash uses a subshell in the first case, but not in the second?

EDIT: after little more digging, the question mostly is a duplicate of this one. A good list of options to handle the issue could be found at http://mywiki.wooledge.org/BashFAQ/024

Community
  • 1
  • 1
Alexander Sergeyev
  • 922
  • 10
  • 19
  • 1
    Your understanding of the subshell is correct, the pipe command runs both sides in separate subshells, when the right side(the loop) terminates all changes to that subshell are lost. < <(command), opens command in a subshell and the loop is in the main one so you keep changes from the while loop. – 123 Dec 08 '16 at 10:46

1 Answers1

1

Not sure if this is a better way than your second code snippet, but a way to solve the first one is to use sub shell { ... } right after the pipe:

declare -A mapping
seq 10 | { 
    while read i; do
       key="key_$i"
       val="val_$i"
       echo "mapping[$key]=$val"
       mapping["${key}"]="${val}"
   done

   echo "${mapping["key_1"]}"
   echo "${mapping["key_2"]}"
}
oliv
  • 12,690
  • 25
  • 45