skip to content

In bash, how do you create an indexed array, append to it, read a single element and get the number of elements — and why does `echo $arr` print only the first element?

level: juniorimportance: must knowfreq 70%

answer

  1. parentheses make a list
  2. bare name means subscript zero
  3. braces are required around subscripts
  4. count versus character length

basics

~10 s

Create with arr=(a b c), append with arr+=(d), read one element as "${arr[0]}", count with "${#arr[@]}". A bare $arr is shorthand for ${arr[0]}, so it shows only the first element.

solid answer

~40 s

An indexed array literal is a parenthesised word list: `arr=(alpha "beta gamma" delta)`. Each word becomes one element, and quoting keeps a multi-word value in a single element. `arr+=(epsilon)` appends after the highest index. You read one element with `${arr[0]}` — the braces are mandatory, because `$arr[0]` expands `$arr` and then leaves a literal `[0]`. `${#arr[@]}` is the element count, while `${#arr}` is the character length of element 0, which is a classic trip-up. `${!arr[@]}` gives the indices, and `${arr[@]:1:2}` takes a two-element slice. Bare `$arr` is defined to mean `${arr[0]}`, so it never prints the whole array; to get every element, expand `"${arr[@]}"`. `declare -p arr` prints the real structure when you are unsure what you have.

code

bash · 9 lines
bash
arr=(alpha "beta gamma" delta)
echo "${arr[0]}"        # alpha
echo "${arr[1]}"        # beta gamma
echo "${#arr[@]}"       # 3  (elements)
echo "${#arr}"          # 5  (characters in alpha)
echo "${#arr[1]}"       # 10 (characters in beta gamma)
arr+=(epsilon)
printf '%s\n' "${arr[@]:1:2}"   # beta gamma / delta
declare -p arr

go deeper

for a junior

Say the four basics without hesitating: arr=(a b c) creates, arr+=(d) appends, "${arr[0]}" reads, "${#arr[@]}" counts. Add that a bare $arr is only the first element.

for a middle

Explain why the braces are mandatory — the subscript is part of the expansion syntax, not of the name — and distinguish ${#arr} (characters in element 0) from ${#arr[@]} (element count).

for a senior

Show the habits that survive real data: quote on the way in and on the way out, use declare -p to inspect element boundaries, and know that an empty array expands to zero words rather than one empty argument.

for a principal

Be ready to say where the shell's single flat list type runs out. When a script needs records, nested structures or keyed lookups threaded through many functions, argue for moving that logic to a real language rather than encoding structure into array elements.

## What an indexed array is An indexed array in bash is a variable that holds many values, each stored under an integer subscript starting at 0. It is the only built-in way to hold a list whose items may contain spaces, tabs or newlines: a plain string variable holding `a b c` is one value that only *looks* like three, and the shell will happily re-split it in ways you did not intend. ## Creating one The compound assignment is a parenthesised list of words: ```bash arr=(alpha "beta gamma" delta) ``` The words inside the parentheses go through the normal expansions, so quoting matters exactly as it does on a command line. `"beta gamma"` is one element; without the quotes it would be two. That gives three elements at indices 0, 1 and 2. You can also assign a single slot directly, `arr[7]=eta`, and bash will create the array for you. `declare -a arr` declares an empty indexed array explicitly, which is useful when a later `+=` is the first thing that touches it. ## Reading one element ```bash echo "${arr[1]}" # beta gamma ``` The braces are not optional. In `$arr[0]`, bash sees the parameter name `arr`, expands it, and leaves `[0]` as literal text, so you get `alpha[0]`. Any time a subscript is involved, the whole thing goes inside `${ }`. The subscript itself is an arithmetic context, so `${arr[i]}` works with a bare variable name and `${arr[n+1]}` does arithmetic. Bash 4.3 and later also accept negative subscripts, where `${arr[-1]}` is the last element. ## Length, count and indices Two expansions look almost identical and mean different things: ```bash echo "${#arr[@]}" # 3 -> number of elements echo "${#arr}" # 5 -> characters in ${arr[0]}, i.e. "alpha" echo "${#arr[1]}" # 10 -> characters in "beta gamma" ``` `${!arr[@]}` lists the subscripts that actually exist, which is the reliable way to iterate when the array has gaps. `${arr[@]:1:2}` is a slice of two elements starting at offset 1; `${arr[@]:1}` takes everything from offset 1 onward. ## Appending ```bash arr+=(epsilon zeta) ``` The `+=` compound assignment appends after the highest existing index — it does not overwrite and it does not care that earlier indices may be missing. This is the idiom for building a list in a loop, one `arr+=("$item")` per iteration, with the item quoted so a value containing spaces stays a single element. ## Why bare $arr is the first element Bash defines a bare array name without a subscript as equivalent to subscript 0. So `echo $arr` prints `alpha` and `arr=x` silently overwrites element 0 while leaving the rest of the array in place. There is no syntax that turns the whole array into a value except `${arr[@]}` and `${arr[*]}`, and in practice you almost always want the quoted `"${arr[@]}"`, which produces one word per element with the contents untouched. The same asymmetry explains why `${#arr}` counts characters: with no subscript, bash is measuring the string in element 0, exactly as it would for any ordinary variable. ## Inspecting what you actually have When an array is behaving oddly, print its real structure instead of guessing: ```bash declare -p arr # declare -a arr=([0]="alpha" [1]="beta gamma" [2]="delta") ``` This shows the element boundaries and the actual subscripts, which is far more informative than `echo`, where the boundaries are invisible. `printf '[%s]\n' "${arr[@]}"` is the quick equivalent: one bracketed line per element makes a stray empty element or an accidentally-merged pair obvious. ## Practical habits Quote on assignment (`arr+=("$line")`), quote on expansion (`"${arr[@]}"`), use `${#arr[@]}` for counts, and reach for `declare -p` when debugging. Those four habits cover nearly everything a script does with a list.

  • What does `arr=x` do to an array that already has three elements?
    It assigns to element 0 only, because a bare array name means subscript 0. Elements 1 and 2 survive untouched, so the array silently ends up with a new first value and the old tail. If you meant to replace the whole thing, use `arr=(x)`, or clear it first with `unset arr`.
  • How do you iterate over an array together with its indices?
    Loop over `"${!arr[@]}"`, which expands to the subscripts that exist, and use each one to read the value: `for i in "${!arr[@]}"; do printf '%s=%s\n' "$i" "${arr[i]}"; done`. This is correct even if some subscripts are missing, whereas a counter running from 0 to `${#arr[@]}-1` is not.
  • What does `${arr[@]:1:2}` give you, and what happens if you ask for more elements than exist?
    It is a slice: up to two elements starting at offset 1. Asking for more than exist is not an error — you simply get however many are available, and an offset past the end yields nothing at all. A leading space is needed for a negative offset, `${arr[@]: -2}`, otherwise bash reads `:-` as the default-value operator.

saying these in an interview costs you the question

  • Thinking $arr expands to the whole array
  • Using ${#arr} to count elements
  • Writing $arr[0] without braces
  • Assuming arr+=x appends a new element
  • Believing an array element cannot contain spaces

context