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 Answers 2

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .