1

Problem: How to get a slice of a bash array's indices?

$ array=('a' 'b' 'c' 'd')
$ echo ${array[@]:0:2}
a b
$ echo ${!array[@]:0:2}
bash: a b c d: invalid variable name

What is exactly happening in the second example and how to get it work like intended?

Context: I want to loop over all array indices but the last, like:

for idx in "${!array[@]:0:${#array[@]}-1}"; do
3
  • There are easier ways to implement this iteration, but what you tried needs an additional variable: indices=(${!array[@]}); for idx in ${indices[@]::${#array[@]}-1}; do … Commented Sep 20 at 9:37
  • Why are you including the ! in the second echo statement: echo ${!array[@]:0:2}? You can create a new variable with with sliced array like so: array_sliced=${array[@]:0:2} and loop over that Commented Sep 20 at 9:41
  • @ASr that would create array_sliced as a scalar, not an array, and then to loop over its contents you'd need to reference it unquoted which becomes very fragile and would fail if the contents included globbing chars, spaces, etc. Commented Sep 21 at 13:41

2 Answers 2

2

If array indices are guaranteed to be continuous you're better off with an arithmetic for loop like this:

for ((i = 0; i < ${#array[*]}-1; i++)); do
  # do something with i
done
Sign up to request clarification or add additional context in comments.

Comments

1

A key indice list is not exactly a bash array!

You can't manipulate a key indice list in same way you do with a bash array.

For doing this, you have to create an (integer) array:

$ array=('a' 'b' 'c' 'd')
$ echo ${array[@]:0:2}
a b
$ declare -i keys=(${!array[@]})
$ echo ${keys[@]:0:2}
0 1

Into a function

showNElem() {
    local number=$1 tmpStr
    local -n varname=$2
    local -ai keys=(${!varname[@]})
    printf -v tmpStr '[%%d]=%q\n' ${varname[@]:0:number}
    printf "$tmpStr" ${keys[@]:0:number}
}

$ array=('a' 'b' 'c' 'd')
$ showNElem 2 array
[0]=a
[1]=b

Note about ${!word...

This bash syntax could have two different form:

  ${!prefix*}
  ${!prefix@}
         Names matching prefix.  Expands to the names of variables  whose
         names begin with prefix, separated by the first character of the
         IFS special variable.  When @ is used and the expansion  appears
         within  double  quotes, each variable name expands to a separate
         word.

  ${!name[@]}
  ${!name[*]}
         List of array keys.  If name is an array  variable,  expands  to
         the  list  of array indices (keys) assigned in name.  If name is
         not an array, expands to 0 if name is set  and  null  otherwise.
         When  @  is used and the expansion appears within double quotes,
         each key expands to a separate word.

So if now I add another variable:

$ array_1=('foo' 'bar baz')
$ echo ${!array*}
array array_1

Could be nice, used with declare -p

$ declare -p ${!array*}
declare -a array=([0]="a" [1]="b" [2]="c" [3]="d")
declare -a array_1=([0]="foo" [1]="bar baz")

Further read: Shift array indexes by 1 (without loop)

1 Comment

I strongly invite you to read: Shift array indexes by 1 (without loop)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.