2

Why does bash appear to count the words in an array rather than the number of elements?

touch '1 red' '2 orange' '3 yellow'        # Three filenames, each of two words

files=( * )                                # Get the set of files
echo "#files is ${#files}"

for (( i=0; i <= ${#files}; i++ ))
do
    printf "%d\t%s\n" $i "${files[$i]}"
done

The output from this seems "wrong". For starters, ${#files} contains 5, showing that there are six elements in ${files[@]}. However, only elements 0, 1, and 2 of ${files[@]} contain any data, and those contain the correct filenames:

#files is 5
0       1 red
1       2 orange
2       3 yellow
3
4
5

I would have expected either ${files[@]} to contain the six words 1, red, 2, orange, 3, and yellow, or ${#files} to be 2. Not this mixed scenario.

Can anyone shed any light on this for me, please?

  • 1
    Try echo "${#files[@]}" instead. – Satō Katsura Oct 12 '16 at 14:27
2

${#files} is the length of the first element of the array, i.e. the length of 1 red. Which is five.

From the manual:

${#name[subscript]} expands to the length of ${name[subscript]}. If subscript is ‘@’ or ‘*’, the expansion is the number of elements in the array. [...]
Referencing an array variable without a subscript is equivalent to referencing with a subscript of 0.

So, ${#files} = ${#files[0]}. But ${#files[@]} or ${#files[*]} give the number of elements in the array.

(It has nothing to do with the number of words, though if you expand ${files[*]} without quotes, you get the usual word splitting over all the values.)

  • Bit of an annoying feature, I'd say, but I guess it's in line with conflating ${array[0]} with $array. – ilkkachu Oct 12 '16 at 14:51
  • If it's counting words in the 0th element, why does ${#files} in my example show 5 not 2 (or 1)? – roaima Oct 12 '16 at 14:54
  • 1
    It's not counting words, just the characters. – ilkkachu Oct 12 '16 at 15:08
2

Please Change ${#files}" to ${#files[@]}"

You are getting 5 from ${#files}" because that is the first file.
Equivalent to ${files[0]}, which is named '1 red'.
That string contains 5 characters.

The loop fails for the same problem, it iterates from 1 to 5.
However, elements 3, 4 and 5 are simply null (unset).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.