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:
${!arr[@]:0:1}
?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 asindirect 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:
arr[@]
0:1
substring from the string obtained from indirect expansionSince 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}
/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With