shell-script zsh array – How to Iterate Over Array Indices in Zsh

arrayshell-scriptzsh

In bash we can iterate over index of an array like this

~$ for i in "${!test[@]}"; do echo $i; done

where test is an array, say,

~$ test=(a "b c d" e f)

so that the output looks like

0
1
2
3

However, when I do the same in zsh I get an error:

➜ ~ for i in "${!test[@]}"; do echo $i; done
zsh: event not found: test[@]

What is going on?

What is the proper way of iterating over indices in zsh?

Best Answer

zsh arrays are normal arrays like in most other shells and languages, they are not like in ksh/bash associative arrays with keys limited to positive integers (aka sparse arrays). zsh has a separate variable type for associative arrays (with keys being arbitrary sequences of 0 or more bytes).

So the indices for normal arrays are always integers 1 to the size of the array (assuming ksh compatibility is not enabled in which case array indices start at 0 instead of 1).

So:

typeset -a array
array=(a 'b c' '')
for ((i = 1; i < $#array; i++)) print -r -- $array[i]

Though generally, you would loop over the array members, not over their indice:

for i ("$array[@]") print -r -- $i

(the "$array[@]" syntax, as opposed to $array, preserves the empty elements).

Or:

print -rC1 -- "$array[@]"

to pass all the elements to a command.

Now, to loop over the keys of an associative array, the syntax is:

typeset -A hash
hash=(
  key1 value1
  key2 value2
  '' empty
  empty ''
)
for key ("${(@k)hash}") printf 'key=%s value=%s\n' "$key" "$hash[$key]"

(with again @ inside quotes used to preserve empty elements).

Though you can also pass both keys and values to commands with:

printf 'key=%s value=%s\n' "${(@kv)hash}"

For more information on the various array designs in Bourne-like shells, see Test for array support by shell

Related Question