0

I have the following example script and want to know what exactly is the length of an array, are this bytes, characters or what else?

#!/bin/bash

# Arrays
# @ vs. *

ape=( "Apple Banana" "Emacs Window" "Panda Bamboo Nature" )
cape=( 'Ping Pong' 'King Kong' 'King Fisher Club' 'Blurb' )
jade=( ally belly cally delly )

echo Expansion with \*
echo ${ape[*]}
echo ${cape[*]}
echo -e "${jade[*]}\n"

echo Expansion with \@
echo ${ape[@]}
echo ${cape[*]}
echo -e "${jade[@]}\n"

echo Elements with \*
echo ${#ape[*]}
echo ${#cape[*]}
echo ${#jade[*]}

echo Elements with \@
echo ${#ape[@]}
echo ${#cape[@]}
echo ${#jade[*]}

echo -e "\nLength"
echo ${#ape}
echo ${#cape}
echo ${#jade}

From the man pages I know, that the array expansion differs from * to @ if the word is double-quoted or not, but I cannot see any differences. Why do I have in both cases the same results?

The output is as follows:

Expansion with *
Apple Banana Emacs Window Panda Bamboo Nature
Ping Pong King Kong King Fisher Club Blurb
ally belly cally delly

Expansion with @
Apple Banana Emacs Window Panda Bamboo Nature
Ping Pong King Kong King Fisher Club Blurb
ally belly cally delly

Elements with *
3
4
4
Elements with @
3
4
4

Length
12
9
4

2 Answers 2

5

You missed the case where it shows that * will expand the array to a single string, and @ expands to individually quoted strings:

printf 'string "%s"\n' "${cape[*]}"

which generates

string "Ping Pong King Kong King Fisher Club Blurb"

and

printf 'string "%s"\n' "${cape[@]}"

which generates

string "Ping Pong"
string "King Kong"
string "King Fisher Club"
string "Blurb"

Remember that echo just concatenates its arguments and prints them, while printf will fill out its format string with the arguments and repeat the same format if more arguments are supplied.

Also,

for s in "${cape[*]}"; do
    echo "$s"
done

generates a single line of output (it only iterates of a single string), while

for s in "${cape[@]}"; do
    echo "$s"
done

generates one per array element.


You always want to use double quotes around the ${array[*]} and ${array[@]} expansions, unless you for some reason want to explicitly invoke word splitting and file name globbing. And you use * or @ depending on whether you need the array elements all together as one string, or individually quoted.

In my experience, one very seldom use [*].


When getting the length of an array, it doesn't matter which of * or @ you use. But if you use neither, you'll get the length in characters of the first element of the array.

4
  • Might be worth pointing out that using ${array[@]} or ${array[*]} unquoted almost never makes sense. Commented Mar 13, 2018 at 15:53
  • In bash, like in ksh (but contrary to all other shells and languages), $array is short for ${array[0]} as opposed to all the elements, so ${#array} is the length of the element of indice 0 (not necessarily the first element, again in bash like in ksh and contrary to most if not all other shells and languages, arrays are sparse). Commented Mar 13, 2018 at 15:56
  • ${array[*]} is handy if you want to create a string from the array with a custom separator. Here's one I use all the time. a=10 b=1 addr=(); for c in {1..5}; do for d in {21..25}; do addr+=("$a.$b.$c.$d"); done; done; aws ec2 describe-network-interfaces --filters "Name=addresses.private-ip-address,Values=[$(IFS=','; echo "${addr[*]}"))]". I could have used printf but I'm old. Commented Mar 9, 2020 at 8:14
  • @Bruce Yes, those kinds of instances is when you'd want to use [*]. Commented Mar 9, 2020 at 8:17
0

Based on the different quoting you used in the variable assignments, I believe you've misinterpreted the phrase "if the word is double-quoted or not" to mean "if the array element(s) are double-quoted", while the manual intends "if the word representing the variable being expanded is double-quoted or not".

You are also using echo with the variable expansions happening on the same line; you might get clearer results if you use something more configurable, such as printf (as Kusalananda did), or as:

$ printf -- '->%s<-\n' "${ape[@]}"
->Apple Banana<-
->Emacs Window<-
->Panda Bamboo Nature<-

If you could see the intermediate values that printf does, you'd see:

printf -- '->%s<-\n' "Apple Banana" "Emacs Window" "Panda Bamboo Nature"

As opposed to the unquoted version:

$ printf -- '->%s<-\n' ${ape[@]}
->Apple<-
->Banana<-
->Emacs<-
->Window<-
->Panda<-
->Bamboo<-
->Nature<-

... where the intermediate values are:

printf -- '->%s<-\n' Apple Banana Emacs Window Panda Bamboo Nature

... which shows exactly what the elements have been expanded to. In the first case, the variable is quoted, so no further word-splitting happens, and you get the three elements back. In the second case, each of those three elements also undergoes word splitting, so printf sees seven elements to print.

The "length" of an array is usually the number of elements, which you've generated with the ${#ape[@]} syntax. Using ${#ape} asks bash for the length of the first element:

Referencing an array variable without a subscript is equivalent to referencing with a subscript of 0

which is why you get those different values.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.