Steve
Steve

Reputation: 81

Bash: Check if key exists in associative array

Goal:

All I want to do is to check if a USER_PARAMETERS_KEYs[j] exists in RPM_PARAMETERS_HASH associative array. I have one array and one associative arrays as the following:

Predefined:

declare -a USER_PARAMETERS_KEYS='([0]="userField" [1]="fUserField" [2]="srcIPField" [3]="srcPortField" [4]="dstIPField" [5]="dstPortField" [6]="dateField" [7]="timeField")'

declare -A RPM_PARAMETERS_HASH='([timeField]="Time" [userField]="User-Name" [dstIPField]="" [srcIPField]="Source-IP-Address" [dstPortField]="Target-UDP-Port" [fUserField]="Full-Name" [dateField]="Date" [srcPortField]="Source-UDP-Port" )'

I implemented the following:

if [[  ${RPM_PARAMETERS_HASH[${USER_PARAMETERS_KEYS[j]}]} ]] ; then

Problem

My problem is when ${USER_PARAMETERS_KEYS[j]} becomes equal to dstIPField since it has an empty string value in the associative array, so the if condition is not satisfied although the key is there.

Upvotes: 5

Views: 6669

Answers (5)

MrPotatoHead
MrPotatoHead

Reputation: 1144

Posting an answer here to add a bit of clarification to answers from @acan and @sailnfool. I found that I needed to run through some example scenarios to full grasp the nuances of the method they both describe (thanks to @sailnfool for the example script you created in your answer). At any rate, my comments here would not have been very legible if I simply replied to @sailnfool's post with a comment, so here goes FBO.

Clarifying the logic/outcomes of @sailnfool's script/answer:

  1. If an array is defined, but a specific element (key) within the array has never been defined: both queries return a result of, "array element does not exist"
  2. If an array element's value previously existed and has been unset: both queries return "array element does not exist"
  3. If an array element's (key's) value is not NULL: both queries return "array element exists"
  4. If an array element's value is NULL: 1st query returns "array element does exist"
  5. If an array element's value is NULL: 2nd query returns "array element does not exist"

I couldn't quite tell from the original question whether the poster was attempting to derive whether or not a particular array element existed (i.e. whether the specific array element had been declared or not) versus whether or not a particular array element had a value (not null).

If the intent is to determine whether or not a specific array element has ever been set (including setting it equal to NULL), then use this method:

[[ ${arr[c]+1} ]] && echo "array key exists" || echo "array key does not exist"

For example:

declare -A arr
arr["c"]=""
[[ ${arr[c]+1} ]] && echo "array key exists" || echo "array key does not exist"

or

declare -A arr
arr["c"]=''
[[ ${arr[c]+1} ]] && echo "array key exists" || echo "array key does not exist"

If a user's intent is to detect when an array element existed and was subsequently unset, or when a specific array element has never been created, either of the two branching methods are going to return the same result. Namely, that the element does not exist. So, re-capping... the only difference between them is the ability of the first method (examples shown above) to branch when an array element exists but is set equal to NULL. While the 2nd branching method will only return a true path (array element exists) when the array element has been defined, and contains a non-null value.

Bottom Line

Your need scenario 1: tell when an array element is defined and = any value, including NULL

Use: 1st branch method [[ ${arr[c]+1} ]] && echo "array key exists" || echo "array key does not exist"

Your need scenario 2: tell me when an array element is defined and contains a non-NULL value

Use: either logic branching method [[ ${arr[c]+1} ]] && echo "array key exists" || echo "array key does not exist" or [[ ${arr[c]:+1} ]] && echo "array key exists" || echo "array key does not exist"

Your need scenario 3: tell me when an array element is not defined at all (i.e. never defined or was defined and subsequently unset)

Use: either logic branching method [[ ${arr[c]+1} ]] && echo "array key exists" || echo "array key does not exist" or [[ ${arr[c]:+1} ]] && echo "array key exists" || echo "array key does not exist"

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 141145

With bash you can just use -v option to [[:

[[ -v RPM_PARAMETERS_HASH["${USER_PARAMETERS_KEYS[j]}"] ]]

Upvotes: 3

acan
acan

Reputation: 11

@sailnfool - kudos to You for finding this solution. The "-v" solution doesn't work for me.

Regarding "+_" - in fact the "operator" is the "+" sign, as states in a "Shell Parameter Expansion" in the Bash Reference Manual:

"${parameter:+word} If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted."

And in the introduction: "Omitting the colon results in a test only for a parameter that is unset."

$ declare -A arr; arr["a"]="x"
$ echo ${arr[a]+_}
_
$ echo ${arr[a]+1}
1
$ echo "|${arr[c]+1}|"
||
$ arr["c"]=''
$ [[ ${arr[c]+1} ]] && echo "ok" || echo "nok"
ok
$ [[ ${arr[c]:+1} ]] && echo "ok" || echo "nok"
nok

Upvotes: 1

sailnfool
sailnfool

Reputation: 61

In the linuxhint article: "Associative Arrays Bash Examples"1 Under Example #5 it shows:

$ if [ ${ArrayName[searchKEY]+_} ]; then echo "Exists"; else echo "Not available"; fi

Note that the "searchKEY" could either be a literal value or a variable that expands to a literal value. Kudos to the author of the article that uncovered this "+_" operator on shell variables as a way to test for the key existence. I was unable to tease this out of the GNU BASH Reference Manual2. I am sure it is related to parameter expansion but I never realized that it would show associative (hash) array member existence.

Upvotes: 0

Tom Fenech
Tom Fenech

Reputation: 74645

It looks like you can make it work by splitting up the steps (first evaluate the key, then use the -v test):

declare -a USER_PARAMETERS_KEYS='([0]="userField" [1]="fUserField" [2]="srcIPField" [3]="srcPortField" [4]="dstIPField" [5]="dstPortField" [6]="dateField" [7]="timeField")'
declare -A RPM_PARAMETERS_HASH='([timeField]="Time" [userField]="User-Name" [dstIPField]="" [srcIPField]="Source-IP-Address" [dstPortField]="Target-UDP-Port" [fUserField]="Full-Name" [dateField]="Date" [srcPortField]="Source-UDP-Port" )'

j=4
key=${USER_PARAMETERS_KEYS[j]}
[[ -v RPM_PARAMETERS_HASH[$key] ]] && echo yes # output: yes

Upvotes: -1

Related Questions