49

Assigning arrays to variables in Bash script seems rather complicated:

a=("a" "b" "c")
b=$a

echo ${a[0]} 
echo ${a[1]}

echo ${b[0]} 
echo ${b[1]} 

leads to

a 
b 
a
   

instead of

a
b
a
b

Why? How can I fix it?

2 Answers 2

79

If you want to copy a variable that holds an array to another name, you do it like this:

a=('a' 'b' 'c')
b=( "${a[@]}" )
Sign up to request clarification or add additional context in comments.

4 Comments

Beware of the sideffects: This renumbers the keys of the array/assoc-array, see: a=(); a[1]=x; a[3]=y; a[5]=z; b=("${a[@]}"); declare -p a b gives declare -A a='([1]="x" [3]="y" [5]="z" )'; declare -a b='([0]="x" [1]="y" [2]="z")' and a=([a]=x [b]=y [c]=z); b=("${a[@]}"); declare -p a b gives declare -A a='([a]="x" [b]="y" [c]="z" )'; declare -a b='([0]="x" [1]="y" [2]="z")' (; added for better readability)
also if a is empty then b will have an item with empty content.
8 years later, but could you please explain what you mean by reassign? According to my tests and the manual, the array contents actually are copied. "${a[@]}" expands to a list of words (one word for each array member), enclosing that list in ( ) makes for a suitable RHS which can be used in an assignment, and b=... finally carries out the assignment. I can't see how a could lose its contents during this, which the term reassign implies. Did I miss something?
You didn't miss anything. I can understand why you have a more nuanced definition of reassign in mind, but I think OP (at least at the time) didn't care about the old name a and just wanted to know how to get all the values from a into b.
24

Why?

If a is an array, $a expands to the first element in the array. That is why b in your example only has one value. In bash, variables that refer to arrays aren't assignable like pointers would be in C++ or Java. Instead variables expand (as in Parameter Expansion) into strings and those strings are copied and associated with the variable being assigned.

How can I fix it?

To copy a sparse array that contains values with spaces, the array must be copied one element at a time by the indices - which can be obtained with ${!a[@]}.

declare -a b=()
for i in ${!a[@]}; do
    b[$i]="${a[$i]}"
done

From the bash man page:

It is possible to obtain the keys (indices) of an array as well as the values. ${!name[@]} and ${!name[*]} expand to the indices assigned in array variable name. The treatment when in double quotes is similar to the expansion of the special parameters @ and * within double quotes.

Here's a script you can test on your own:

#!/bin/bash

declare -a a=();
a[1]='red hat'
a[3]='fedora core'

declare -a b=();

# Copy method that works for sparse arrays with spaces in the values.
for i in ${!a[@]}; do
    b[$i]="${a[$i]}"
done

# does not work, but as LeVar Burton says ...
#b=("${a[@]}")

echo a indicies: ${!a[@]}
echo b indicies: ${!b[@]}

echo "values in b:"
for u in "${b[@]}"; do
    echo $u
done

Prints:

a indicies: 1 3
b indicies: 1 3  # or 0 1 with line uncommented
values in b:
red hat
fedora core

This also works for associative arrays in bash 4, if you use declare -A (with capital A instead of lower case) when declaring the arrays.

3 Comments

b=( "${a[@]}" ) copies elements with spaces. See Special Parameters
Yes, but it doesn't support sparse arrays.
This one got me too - I imagined a sparse array as meaning missing values, unset a; a=( x '' '' y ), but on a re-read I now see "sparse" in this context is being used to mean missing indices unset a; a[0]=x; a[3]=y. Use b=( "${a[@]}" ); declare -p a b after each of those a initializations to see the difference. The solution copies array values, not whole arrays.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.