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.

elbarna
  • 12,695

7 Answers7

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
mr.spuratic
  • 9,901
  • 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 May 12 '21 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 May 12 '21 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
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

Rosta Kosta
  • 359
  • 2
  • 4
  • 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 Dec 22 '20 at 10:55
  • what about just adding spaces to if? like: if [[ " ${array[@]} " =~ " ${value} " ]]; then – Oleg Abrazhaev Jun 23 '21 at 13:06
  • @AdminBee, maybe this one will help? https://unix.stackexchange.com/a/403560/140177 – ssi-anik Nov 27 '23 at 05:58
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.

  • 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 Mahmoudian Mar 24 '23 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. – Stéphane Chazelas Mar 24 '23 at 21:15
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
    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. – Stéphane Chazelas Nov 09 '17 at 16:58
  • yes, it is applicable on the simple arrays like the one in the question. – Petr Ketner Nov 10 '17 at 14:23
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
ling
  • 411
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