Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting first index of array

Tags:

arrays

linux

bash

I have an array that is not indexed from 0:

arr=([2]=aaa bbb ccc ddd)

I need to get the first index of the array. There are a lot of things I have tried and that work:

for index in "${!arr[@]}"; do
    first_index=$index
    break
done
first_index=$(awk '{ print $1 }' <<< "${!arr[@]}")
first_index=$(cut -d' ' -f1 <<< "${!arr[@]}")
first_index=${!arr[@]}
first_index=${first_index%% *}
ind=("${!arr[@]}")
first_index=${ind[@]:0:1}

What I really wanted to work:

${!arr[@]:0:1}

Given that this syntax works so well with arrays ${arr[@]:0:1} and is very clean:

  1. Is there a similar, cleaner way to do this for indexes as well without external tools, temporary arrays, temporary variables, loops etc? My attempts seem to overcomplicate such an easy task
  2. What is actually happening in ${!arr[@]:0:1}?
like image 720
mickp Avatar asked Oct 17 '22 01:10

mickp


1 Answers

My attempts seem to overcomplicate such an easy task

The task might look easy, but might not be a common use case and therefore have no syntactic sugar to make it easy, especially if you can use other built-in features to accomplish it.

You have plenty of alternatives, here's one more:

put_first_index_in () {
    printf -v "${1?}" "%s" "${2?}"
}

put_first_index_in first_index "${!arr[@]}"

What is actually happening in ${!arr[@]:0:1}?

Indirect variable expansion:

If the first character of parameter is an exclamation point (!), and parameter is not a nameref, it introduces a level of variable indirection. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion.

With ! as the first character, three possible things are supported: ${!parameter} (the indirect expansion given above), ${!prefix*}, ${!prefix@} (expand to variable names matching the prefix), and ${!name[*]}, ${!name[@]} (expand to indexes of the name array).

The docs suggest that only ${!parameter} supports further substitution, since it is only mentioned for this and not for the others. So bash tries to do the following:

  1. Expand arr[@]
  2. Apply indirect expansion on the string obtained from (1)
  3. Get the 0:1 substring from the string obtained from indirect expansion

Since is not a valid character in identifiers, we get that error:

$ foo=SHELL
$ echo ${!foo}
/bin/zsh
$ echo ${!foo:0:1}
/
$ foo="SHELL "
$ echo ${!foo}
bash: SHELL : bad substitution
$ arr=([2]=SHELL bbb ccc ddd)
$ echo ${!arr[@]:0:1}
bash: SHELL bbb ccc ddd: bad substitution

Thus, this will only work with arrays of a single element, say:

$ arr=([2]=SHELL)

And as expected:

$ echo ${!arr[@]:0:1}
/
like image 193
muru Avatar answered Oct 23 '22 23:10

muru