Some Notes on ZSH Arrays

Here is a summary of the rules for substitution; this assumes that braces are present around the substitution, i.e. ${...}. Some particular examples are given below. Note that the Zsh Development Group accepts no responsibility for any brain damage which may occur during the reading of the following rules.

I'm doing inadvisably complicated things with zsh again; you'll need this to use it as well, if you dare. More on that in due course. What I'm here to write about now is the zsh part, and the parts of that part (die sich das Licht gebar) that were a struggle to get right, even with a quite useful cheatsheet.

This is a six-element zsh array, extracted from its natural habitat in a function (note the local):

local MYARRAY=("alpha beta" gamma delta gamma "epsilon" "alpha beta")

1. Deduplication

This turned out to be easy.

typeset -U MYARRAY

Done and dusted. It's also possible with a parameter expansion flag, though. Sometimes.

echo ${(u)MYARRAY} # alpha beta gamma delta epsilon
echo "${(u)MYARRAY}" # alpha beta gamma delta gamma epsilon alpha beta

See, outside a string ${} does parameter expansion, which applies to things like arrays. Inside a string, ${} is a brace expansion and your flags mean nothing.

2. Passing Arrays to Functions

function otherfunction() {
  # local ARR=???
  echo "${#ARR} elements in $ARR[@]" # print count and contents
}

function main() {
  ....
  otherfunction MYARRAY
}

main

Okay, remember the parentheses in the function signature are a total red herring, arguments are numbered. Let's try filling in that blank the simplest possible way:

  local ARR=$1 # 7 elements in MYARRAY

Nope, that passed the variable name in as a string. We've got to use parameter expansion, specifically the P flag to interpret the value as a parameter name and the A flag to indicate it's an array. Take two:

  local ARR=${(PA)1} # 30 elements in alpha beta gamma delta epsilon

Well, we have the expected contents, but it's also obviously a string: 30 elements! The secret is to reconstitute the array into an array:

  local ARR=(${(P)1}) # 4 elements in alpha beta gamma delta epsilon

Success! The A flag can be included or not -- it makes no difference whatsoever.

3. Also, Watch Your Scopes

for TARGET in "${MYARRAY[@]}"; do
  if [ -n "$TARGET" ]; then echo "$TARGET is real!"; fi
done

If TARGET already contains a value you get a free spin through the loop that you probably don't want!