3

This is my script (zsh 5.8):

# Consider parameter expansion of an unset variable to be an error
set -u

Define and populate associative array

typeset -A arr arr=(x xxx y yyy)

echo $arr[x] # Outputs xxx

Test whether key x is present

((${arr[(Ie)x]})) && echo true

The last line gives me the error zsh: x: parameter not set, and from this message I conclude that zsh treats the x not literally, but as a variable which zsh wants to expand. Why do I have a variable expansion here, and how can I write test (without violating set -u) for checking that the key x is present in the array?

I know that I can of course write an explicit loop over the keys (i.e. for k in ${(k)arr}, but I suspect that there must be an easier way to accomplish this.

2 Answers2

3

Since the key x is present in arr, ${arr[(Ie)x]} expands to the list of keys that match x, which is just x. This results in the arithmetic expression x, which evaluates to the numerical value of x. But x is unset, hence the error that you see.

Generally, be careful with associative arrays in arithmetic expressions.

[[ $arr[(Ie)x] ]] would work for a non-empty key. Alternatively, ((${+arr[x]})) is more common, but I don't see any advantage to it: it also doesn't work with empty keys. I think that the $+ approach works for arbitrary keys if you use an intermediate variable:

key=x # or '' or ']' …
(($+arr[$key]))
2

With associative arrays in Zsh, you can test whether the key exists with [[ -v dict[key] ]]. You can read about the conditional flags like -v here.

Remember, when using -v, don't prefix your dict variable with a $. You are checking the named variable exists, not the value it may contain.

Here's a simple example:

$ # declare an associative array
$ typeset -A fruits=(banana yellow strawberry red blueberry blue)
$ # print out all the keys with (k) and values with (v)
$ echo ${(k)fruits}
banana strawberry blueberry

Now, let's show how -v works:

if ! [[ -v fruits[apple] ]]; then
  echo "no apples here"
fi

if [[ -v fruits[banana] ]]; then echo "monkey business" fi

for k in ${(k)fruits} orange pineapple; do [[ -v fruits[$k] ]] && echo "$k exists" || echo "$k missing" done

This will output:

no apples here
monkey business
banana exists
strawberry exists
blueberry exists
orange missing
pineapple missing
mattmc3
  • 237