1

Is there a way to check dictionary is declared in the shell?

There is a way to check variable is not empty, and there is a way to check that dictionary has a key, but not sure what is the proper way of checking that dictionary exists.

I guess I've found the solution

declare -A dictionary
dictionary[key]="val"

if [[ -z $(declare -p dictionary 2> /dev/null) ]]
then
  echo no
else
  echo yes
fi

But maybe there is a more idiomatic one?

Kos
  • 1,547
  • 14
  • 23
  • 1
    Please edit your question to include sample code that demostrates what you mean by "variable is not empty", "dictionary has a key". What are you calling a "dictionary"? This is not common terminology in bash documentation (although I can/have to guess what you mean). Good luck. – shellter Jan 16 '23 at 19:32
  • You're talking about an associative array, right? Python uses "dictionary" (`dict`) to refer to something similar. – wjandrea Jan 16 '23 at 19:34
  • Related? [How do I check if variable is an array?](/q/14525296/4518341) – wjandrea Jan 16 '23 at 19:37
  • `declare -A foo` marks the *name* to refer to an associative array, but the array itself does not exist until you add at least one key to it. `foo=()` does not actually assign any value to the name `foo`, leaving it unsets. – chepner Jan 16 '23 at 19:38
  • Dictionary: https://devhints.io/bash#dictionaries – Kos Jan 16 '23 at 19:45
  • @chepner this `declare -A dicto; declare -p dicto; printf "%s\n" "${#dicto[*]}" "'${dicto[@]}'"` shows `declare -A dicto 0 ''` so seems it's set somehow even if empty. – LMC Jan 16 '23 at 20:05
  • 2
    Parameter expansion always produces an empty string for all unset variables. – chepner Jan 16 '23 at 20:07
  • I would say `[[ -v dictionary ]]` is the correct way to determine if it is defined, but that's false even if the array *does* have keys. It would appear that the name `dictionary` itself is never set, only indexed expressions like `dictionary[key]`. This makes sense if you accept that arrays don't exist as first-class values. Array syntax just allows you to "simulate" arrays as collections of values accessible via a single name. – chepner Jan 16 '23 at 20:12
  • Unlike an indexed array, where `$d` and `${d[0]}` are virtually synonymous, `$dictionary` produces no value, no matter how many keys have been assigned values. – chepner Jan 16 '23 at 20:14
  • Good show for adding your code! Getting some good advice here. Good luck to all. – shellter Jan 16 '23 at 20:23

3 Answers3

2

Your declare -p trick is probably the only way to know that a variable has been marked with declare -A before anything is assigned to it (edit: Prior to Bash 5.0). The variable itself is still unset until you insert data into the array, so typical tricks using things like [[ -z ${var+x} ]] won't work.

If you have Bash 5.0 or beyond, there is apparently an expansion to test for an associative array.

$ declare x; echo "${x@a}"

$ declare -a y; echo "${y@a}"
a
$ declare -A z; echo "${z@a}"
A

To check that an array has keys, you can use:

#!/bin/bash
declare -A arr1 arr2
arr1=( ["foo"]=bar )
if [[ "${!arr1[@]}" ]] ; then
  echo "arr1 has keys"
fi
if [[ "${!arr2[@]}" ]] ; then
  echo "arr2 has keys"
fi
# prints "arr1 has keys"
tjm3772
  • 2,346
  • 2
  • 10
  • And to check if a particular key exists in the array: `[[ -v arr2["foo bar"] ]]` – glenn jackman Jan 16 '23 at 20:05
  • 1
    `[[ "${!arr1[@]}" ]]` will expand to multiple strings if array has more than 1 key. It would be safer to test `[[ -n "${!arr1[*]}" ]]` because it has all keys concatenated, and expanded into a single string. – Léa Gris Jan 16 '23 at 20:18
  • 1
    In an extended test `[[ "${!arr1[@]}" ]]` is safe because the result of parameter expansion doesn't undergo word splitting, but worth attention if you're not using extended tests for some reason. – tjm3772 Jan 16 '23 at 20:45
2

Multiple ways without using a sub-shell:

if declare -p dictionary 2> /dev/null
then printf 'dictionary is declared!\n'
fi

To be sure dictionary is declared and is an Associative array:

if [[ "${dictionary@a}" = *A* ]]
then printf 'dictioary is declared as an Associative array!\n'
fi
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • `if [[ "${dictionary@a}" = *A* ]]` does not seem working for me – Kos Jan 16 '23 at 20:21
  • `if declare -p dictionary 2> /dev/null` also does not work with my bash `GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)` – Kos Jan 16 '23 at 20:22
  • Would help to post a sample of what you tried, both examples work on 5.0.17 when I test them. – tjm3772 Jan 16 '23 at 22:21
0

I guess I've found the solution

declare -A dictionary
dictionary[key]="val"

if [[ -z $(declare -p dictionary 2> /dev/null) ]]
then
  echo no
else
  echo yes
fi

But maybe there is a more idiomatic one?

Kos
  • 1,547
  • 14
  • 23