Iterate and Check if a Bash Array contains a value

  • HOME
  • >
  • BASH
  • >
  • Iterate and Check if a Bash Array contains a value
Last Updated: 
Tags:  bash array

Version 2 of GNU Bash added support for array variables, also known as one-dimensional indexed arrays, or lists. Since version 4, came the support for associative arrays, also known as dictionaries or hash tables. Those features simplifies heavily how you can write your scripts and support more complex logics and use cases.

👉 Many fixes and improvements have been made with Bash version 5, read more details with the post What's New in GNU Bash 5?

In this post we cover how to declare, iterate over, check, and delete a value in an indexed array and associative array.

Difference between Indexed Arrays and Associative Arrays

First and foremost, you need to differentiate clearly the two types of arrays that can be used in bash. An indexed array is an array in which the keys (indexes) are ordered integers. You can think about it as an ordered list of items. Then, an associative array, a.k.a hash table, is an array in which the keys are represented by arbitrary strings.

How to declare an Array in Bash?

Arrays in Bash are one-dimensional array variables. The declare shell builtin is use to declare array variables and give them attributes using the -a and -A options. Note that there is no upper limit (maximum) on the size (length) of a Bash array and the values in an Indexed Array and an Associative Array can be any strings or numbers, with the null string being a valid value. Remember that the null string is a zero length string, which is an empty string.

Indexed Array (ordered lists)

You can create an Indexed Array on the fly using compound assignment or by using the builtin command declare.

[me@host ~]$ myIndexedArray=(one two three)
[me@host ~]$ echo ${myIndexedArray[*]}
one two three

[me@host ~]$ myIndexedArray[5]='five'
[me@host ~]$ echo ${myIndexedArray[*]}
one two three five

[me@host ~]$ myIndexedArray[4]='four'
[me@host ~]$ echo ${myIndexedArray[*]}
one two three four five

[me@host ~]$ myIndexedArray+=('six')
[me@host ~]$ echo ${myIndexedArray[*]}
one two three four five six

With the declare built-in command and the lowercase "-a" option, you would simply do the following:

[me@host ~]$ declare -a mySecondIndexedArray
[me@host ~]$ mySecondIndexedArray[0]='zero'
[me@host ~]$ echo ${mySecondIndexedArray[*]}
zero

Associative Array (dictionnaries, hash table, or key/value pair)

You cannot create an associative array on the fly. You can only use the declare built-in command with the uppercase "-A" option.

[me@host ~]$ declare -A myAssociativeArray
[me@host ~]$ myAssociativeArray[a]=123
[me@host ~]$ myAssociativeArray[b]=456
[me@host ~]$ myAssociativeArray+=([c]=789 [d]=012)
[me@host ~]$ echo ${myAssociativeArray[*]}
012 789 456 123

⚠️ Do not confuse -a (lowercase) with -A (uppercase). It would silently fail. Indeed, declaring an Indexed array will accept subscript but will ignore it and treat the rest of your declaration as an Indexed Array, not an Associative Array.

👉 Read more about the common error "Bash Error: must use subscript when assigning associative array".

Array Operations

For Loop: How to iterate over a Bash Associative Array

As we saw above, we can access all the values of an array using the * (wildcard) notation. Though, to iterate through all the array values you should use the @ (at) notation instead. The difference between the two will arise when you try to loop over such array using quotes. The * notation will return all the elements of the array as a single word result while the @ notation will return a value for each elements of the array as a separate word. This becomes clear when performing a for loop on such variable.

[me@host ~]$ myDemoArray=(1 2 3 4 5)

# Using '*'
[me@host ~]$ echo ${myDemoArray[*]}
1 2 3 4 5

# Using '@'
[me@host ~]$ echo ${myDemoArray[@]}
1 2 3 4 5

# For Loop Exampe with '*', will echo only once all the values
[me@host ~]$ myDemoArray=(1 2 3 4 5)
[me@host ~]$ for value in "${myDemoArray[*]}"; do echo "$value"; done
1 2 3 4 5

# For Loop Example with '@', will echo individually each values
[me@host ~]$ myDemoArray=(1 2 3 4 5)
[me@host ~]$ for value in "${myDemoArray[@]}"; do echo "$value"; done
1
2
3
4
5

Obtain Keys (Indices): How to get the Key/Value pair of an Array?

When looping over an array it's often useful to access the keys of the array separately of the values. This can be done by using the ! (bang) notation.

# Print Indexed Array Keys
[me@host ~]$ for keys in "${!myDemoArray[@]}"; do echo "$keys"; done
0
1
2
3
4

# Print Associative Array Values
[me@host ~]$ myAssociativeArray=([a]=123 [b]=456)
[me@host ~]$ for value in "${myAssociativeArray[@]}"; do echo "$value"; done
456
123

# Print Associative Array Keys
[me@host ~]$ myAssociativeArray=([a]=123 [b]=456)
[me@host ~]$ for keys in "${!myAssociativeArray[@]}"; do echo "$keys"; done
b
a

# Iterate over key and value of an Associative Array
[me@host ~]$ myAssociativeArray=([a]=123 [b]=456)
[me@host ~]$ for key in "${!myAssociativeArray[@]}"
> do
>   echo -n "key  : $key, "
>   echo "value: ${myAssociativeArray[$key]}"
> done
key  : b, value: 456
key  : a, value: 123

Array Length: How to get a Bash Array size?

Another useful aspect of manipulating Bash Arrays is to be able to get the total count of all the elements in an array. You can get the length (ie size) of an Array variable with the # (hashtag) notation.

[me@host ~]$ myArray=(a b c d)
[me@host ~]$ echo "myArray contain ${#myArray[*]} elements"
myArray contain 4 elements

[me@host ~]$ myAssociativeArray=([a]=123 [b]=456)
[me@host ~]$ echo "myAssociativeArray contain ${#myAssociativeArray[*]} elements"
myAssociativeArray contain 2 elements

Delete: How to remove a key from a Bash Array or delete the full array?

The unset bash builtin command is used to unset (delete or remove) any values and attributes from a shell variable or function. This mean that you can simply use it to delete a Bash array in full or only remove part of it by specifying the key. unset take the variable name as argument, so don't forget to remove the $ (dollar) sign in front of the variable name of your array. See the complete example below.

[me@host ~]$ declare -A myArray=([one]=un [two]=deux [three]=trois)
[me@host ~]$ echo ${myArray[*]}
deux trois un
[me@host ~]$ echo ${myArray[one]}
un
[me@host ~]$ unset myArray[one]
[me@host ~]$ echo ${myArray[*]}
deux trois
[me@host ~]$ unset myArray
[me@host ~]$ echo ${myArray[*]}

Test Value In Array: How to Check if a Bash Array contains a value?

In most cases, you can probably use the binary operator =~. The string to the right of the operator is considered a POSIX extended regular expression and matched accordingly. Though, this would not look for an exact match, it is a regex.

[me@host ~]$ myArray=(a b c d)
[me@host ~]$ [[ ${myArray[*]} =~ 'a' ]] && echo 'yes' || echo 'no'
yes
[me@host ~]$ [[ ${myArray[*]} =~ 'e' ]] && echo 'yes' || echo 'no'
no

# Return True even for partial match
[me@host ~]$ myArray=(a1 b1 c1 d1 ee)
[me@host ~]$ [[ ${myArray[*]} =~ 'a' ]] && echo 'yes' || echo 'no'
yes
[me@host ~]$ [[ ${myArray[*]} =~ 'a1' ]] && echo 'yes' || echo 'no'
yes
[me@host ~]$ [[ ${myArray[*]} =~ 'e' ]] && echo 'yes' || echo 'no'
yes
[me@host ~]$ [[ ${myArray[*]} =~ 'ee' ]] && echo 'yes' || echo 'no'
yes
[me@host ~]$ echo ${myAssociativeArray[*]}
456 123
[me@host ~]$ [[ ${myAssociativeArray[*]} =~ 3 ]] && echo 'yes' || echo 'no'
yes
[me@host ~]$ [[ ${myAssociativeArray[*]} =~ 123 ]] && echo 'yes' || echo 'no'
yes
[me@host ~]$ [[ ${myAssociativeArray[*]} =~ 1234 ]] && echo 'yes' || echo 'no'
no

In order to account for an exact match, your regex pattern needs to account for it by adding extra space before and after the value like (^|[[:space:]])"VALUE"($|[[:space:]]).

[me@host ~]$ [[ ${myAssociativeArray[*]} =~ (^|[[:space:]])"12"($|[[:space:]]) ]] && echo 'yes' || echo 'no' 
no
[me@host ~]$ [[ ${myAssociativeArray[*]} =~ (^|[[:space:]])"123"($|[[:space:]]) ]] && echo 'yes' || echo 'no' 
yes

With Associative Arrays, you can extend the solution to test values with [[ -z "${myArray[$value]}" ]].

# delete previously set declaration of myArray and
# prevent the error `bash: myArray: cannot convert indexed to associative array`
[me@host ~]$ unset myArray 

[me@host ~]$ declare -A myArray=([one]=un [two]=deux [three]=trois)
[me@host ~]$ echo ${myArray[*]}
deux trois un

[me@host ~]$ for value in one two three four
> do
>     echo -n "$value is "
>     [[ -z "${myArray[$value]}" ]] && echo -n '*not* '
>     echo "a member of ( ${!myArray[*]} )"
> done
one is a member of ( two three one )
two is a member of ( two three one )
three is a member of ( two three one )
four is *not* a member of ( two three one )
Related bash posts that you may like
What is the Bash Null Command?
Learn about the Bash null command, also known as the POSIX shell colon command. This post cover concrete use cases and pitfalls to avoid.
How To Format Date and Time in Linux, macOS, and Bash?
Find out how to manipulate date and time on linux and macOS systems as well as natively in the Bash shell. This post covers all you need to know to format a date from your shell.
How To Use Option as Meta Key in macOS Terminal?
The Meta Key is a modifier key that can be quite helpful to improve your productivity while working in a terminal and bash. This post cover how to enable from the command line the Meta Key in macOS Terminal.