30

Help for a simple script

#!/bin/bash

array1=(
prova1
prova2
slack64
)

a="slack64"
b="ab"

if [ $a = $b ]
then
      echo "$a = $b : a is equal to b"
else
      echo "$a = $b: a is not equal to b"
fi

This script simply doesn't work, I want a script which check if slack64 is present in a list(i use an array),and simply give me, yes is present,or no. I don't know how to compare an array with a single variable.

1
  • 6
    Where does the array1 work?
    – tachomi
    Commented Jan 3, 2015 at 3:38

7 Answers 7

33

Use a different kind of array: rather than an integer-indexed array, use an associative array, so the key (index) is what you will be checking for. bash-4.0 or later is required for this.

declare -A array1=( 
 [prova1]=1  [prova2]=1  [slack64]=1
)

a=slack64
[[ -n "${array1[$a]}" ]] && printf '%s is in array\n' "$a"

In the above we don't really care about the values, they need only be non-empty for this. You can "invert" an indexed array into a new associative array by exchanging the key and value:

declare -a array1=( 
 prova1 prova2 slack64
)
declare -A map    # required: declare explicit associative array
for key in "${!array1[@]}"; do map[${array1[$key]}]="$key"; done  # see below

a=slack64
[[ -n "${map[$a]}" ]] && printf '%s is in array\n' "$a"

This can pay off if you have large arrays which are frequently searched, since the implementation of associative arrays will perform better than array-traversing loops. It won't suit every use case though, since it cannot handle duplicates (though you can use the value as a counter, instead of just 1 as above), and it cannot handle an empty index.

Breaking out the complex line above, to explain the "inversion":

for key in "${!a[@]}"     # expand the array indexes to a list of words
do 
  map[${a[$key]}]="$key"  # exchange the value ${a[$key]} with the index $key
done
2
  • 1
    The indexed-to-associative code is a tad more complex than necessary since bash can just expand the indexed array to values. for v in "${array1[@]}"; do map[$v]=1; done is easier to read, and probably imperceptibly faster with a very large array since it doesn't have to generate a list of indices and then do an array lookup for every assignment. ;)
    – dannysauer
    Commented May 12, 2021 at 22:30
  • 1
    Confirming suspicions: Creating a big array with declare -a array1=($(seq 1 100000)), for example, I usually get about the same times to run both declare -A b=(); time for v in "${array1[@]}"; do b[$v]=1; done v/s declare -A b=(); time for k in "${!array1[@]}"; do b[${array1[$k]}]=1; done, so I guess I retract my claim that it should be faster. :D
    – dannysauer
    Commented May 12, 2021 at 22:42
18

The straightforward way is to iterate with a loop :

var=ab
for item in "${array[@]}"; do
    [[ $var == "$item" ]] && echo "$var present in the array"
done
4
  • SO the OP should be informed that he have to take care if the values can contain special characters, like [ ] : character class (globs) Commented Jan 5, 2015 at 11:15
  • In line 3, the $var should be put in double quotes as well; otherwise an empty value would cause a syntax error (if you like to check for another value than 'ab'). (Sorry, an edit requires 6+ characters to change ...)
    – Tobias
    Commented Mar 2, 2023 at 11:19
  • 1
    No, no need to quote in [[ ]] See <mywiki.wooledge.org/BashFAQ/031> and <mywiki.wooledge.org/BashGuide/TestsAndConditionals> Commented Mar 2, 2023 at 11:31
  • 1
    Ah, interesting: "Since [[ isn't a normal command (like [ is), but a shell keyword, it has special magical powers." Thank you!
    – Tobias
    Commented Dec 7, 2023 at 15:49
10

How to check if a Bash Array contains a value


False positive match

array=(a1 b1 c1 d1 ee)

[[ ${array[*]} =~ 'a' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'a1' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'e' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'ee' ]] && echo 'yes' || echo 'no'
# output:
yes

Exact match

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

# Exact match

array=(aa1 bc1 ac1 ed1 aee)

if [[ ${array[*]} =~ (^|[[:space:]])"a"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
No

if [[ ${array[*]} =~ (^|[[:space:]])"ac1"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
Yes

find="ac1"
if [[ ${array[*]} =~ (^|[[:space:]])"$find"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
Yes

For more usage examples the source of examples are here

3
  • Welcome to the site, and thank you for your contribution. Please note that even the "exact match" approach will fail if you have array elements that contain spaces, such as array=(aa1 bc1 "ac1 ed1" aee); you may want to include safeguards against that in your answer.
    – AdminBee
    Commented Dec 22, 2020 at 10:55
  • what about just adding spaces to if? like: if [[ " ${array[@]} " =~ " ${value} " ]]; then Commented Jun 23, 2021 at 13:06
  • @AdminBee, maybe this one will help? unix.stackexchange.com/a/403560/140177
    – ssi-anik
    Commented Nov 27, 2023 at 5:58
9

You can also use grep for that:

array1=(prova1 prova2 slack64)
a=slack64
if (printf '%s\n' "${array1[@]}" | grep -xq $a); then
    echo "it's in"
fi
2
  • 2
    That assumes the array elements don't contain newline characters (and blanks and wildcards as you forgot to quote $a, and don't start with - as you forgot the --). You could use %s\0 instead of %s\n and use grep --null (assuming GNU grep or compatible) as bash variables can't contain the NUL character anyway. You'd also need to handle the case of an empty array specially (as that printf command would print the same thing as for an array with one empty element). Also note that you don't need to start a subshell here. Commented Nov 9, 2017 at 16:58
  • yes, it is applicable on the simple arrays like the one in the question. Commented Nov 10, 2017 at 14:23
9

With zsh:

array1=(
  prova1
  prova2
  slack64
)

a=slack64

if (( $array1[(Ie)$a] )); then
  printf '%s\n' "$a in array1"
fi

The I array subscript flag is for returning the index of the rightmost element that matches $a (or 0 if not found); with e, it's an exact / literal match, not pattern matching.


With bash:

Since bash's array members can't contain NUL bytes (contrary to zsh's), if you have GNU grep or compatible you can always do:

if (( ${#array1[@]} )) && printf '%s\0' "${array1[@]}" | grep -zqxFe "$a"; then
  printf '%s\n' "$a in array1"
fi

That is, print the elements NUL-delimited and ask grep to quietly find exact Fixed-string matches for the expression stored in $a in those zero-delimited records.

2
  • This was the answer I chose and used due to being POSIX compatible. But as a general comment, I don't think the grep needs anything other than -q
    – Mehrad
    Commented Mar 24, 2023 at 19:34
  • @MehradMahmoudian, not sure what you mean. There's hardly anything POSIX in there. zsh, bash, arrays, -z, or the ability for grep to work on non-text input are not POSIX. grep -q -- "$regex" returns true if any line of its input matches the regexp, I can't see how that could help find if an element is present in an array. Commented Mar 24, 2023 at 21:15
4

This function works with associative arrays.

We can use this function to do one of the following:

-- check if the array has a certain key -----------------> inArray "myKey" ${!myArray[@]}"

-- check if the array contains a certain value ---------> inArray "myValue" "${myArray[@]}"

function inArray # ( keyOrValue, arrayKeysOrValues ) 
{
  local e
  for e in "${@:2}"; do 
    [[ "$e" == "$1" ]] && return 0; 
  done
  return 1
}

declare -A arr
arr[doo]=55

inArray "doo" "${!arr[@]}"
echo $?     # 0 
inArray "bla" "${!arr[@]}"
echo $?     # 1 
inArray "55" "${arr[@]}"
echo $?     # 0
1

You can use "grep".

_array=(a b c)
_find_me="b"

echo ${_array[@]} | grep "$_find_me" && \
    echo "I found: $_find_me"        || \
    echo "I could not found: $_find_me"

Output:

a b c
I found: b

You must log in to answer this question.

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