77

I am looking for a construct in bash, to decide if a variable $WORD is one of defined words. I need something like this:

if "$WORD" in dog cat horse ; then 
    echo yes
else
    echo no
fi

does bash have such construct? If not, what would be the closest?

12 Answers 12

87

This is a Bash-only (>= version 3) solution that uses regular expressions:

if [[ "$WORD" =~ ^(cat|dog|horse)$ ]]; then
    echo "$WORD is in the list"
else
    echo "$WORD is not in the list"
fi

If your word list is long, you can store it in a file (one word per line) and do this:

if [[ "$WORD" =~ $(echo ^\($(paste -sd'|' /your/file)\)$) ]]; then
    echo "$WORD is in the list"
else
    echo "$WORD is not in the list"
fi

One caveat with the file approach:

  • It will break if the file has whitespace. This can be remedied by something like:

    sed 's/[[:blank:]]//g' /your/file | paste -sd '|' /dev/stdin
    

Thanks to @terdon for reminding me to properly anchor the pattern with ^ and $.

4
  • 2
    And shopt -s nocasematch might help if you want the search to be case insensitive. – Skippy le Grand Gourou Feb 13 '14 at 16:27
  • 1
    Note that you have to use [[ and ]] - [ and ] are not enough. – Greg Dubicki Aug 26 '16 at 11:28
  • 1
    i was searching for a 'oneliner' to validate my script argument, and this worked perfectly. thank you! [[ "$ARG" =~ ^(true|false)$ ]] || { echo "Argument received invalid value" ; exit 1 ; } – RASG May 17 '17 at 11:20
  • If you're going to put your list of matches in a file, you might as well use grep: egrep -q "^$WORD$" /tmp/words; [[ $? != 0 ]] && echo "Not found" || echo "OK". – GLRoman Aug 21 '20 at 21:57
100
case $word in
    dog|cat|horse) echo yes;;
    *)             echo no;;
esac
0
13

How about:

#!/usr/bin/env bash

WORD="$1"
for w in dog cat horse
do
  if [ "$w" == "$WORD" ] 
  then
      yes=1
      break
  fi
done;
[ "$yes" == "1" ] && echo "$WORD is in the list" || 
                     echo "$WORD is not in the list"

Then:

$ a.sh cat
cat is in the list
$ a.sh bob
bob is not in the list
5
if (echo "$set"  | fgrep -q "$WORD")
3
  • 1
    Careful though, this will return true if $WORD is empty, it will match if WORD=ca or WORD=og or similar and I assume you meant echo ${set[@]}. – terdon Jan 29 '14 at 18:25
  • 2
    just add -w to grep to avoid partial words – BrenoZan Jan 29 '14 at 18:34
  • Maybe you should edit your answer to add the -w there because it's likely that the OP wanted exact words, not partial. – Alexis Wilke Apr 14 at 4:37
4

I just had the same problem and found a surprisingly understandable solution which does not require any regex knowledge.

Please note that the solution shown below might be bash-only, because I have no clue about the differences between bash and other shells. Furthermore, the bash manual warns that this sort of pattern matching is slow if the string which should be matched is long.

WORD=abc
[[ "$WORD" == @(def|abc|ghi|foo|barbaz) ]] && echo Word is in set.

How does this work? I don't want to repeat large portions of the bash manual here, but in my own words:

If the binary operator == is used with the compound command [[, the word to the right side of the operator undergoes pattern matching as explained in the section "Pattern Matching" in the bash manual. Furthermore, under these conditions, while matching, the shell option extglob is assumed to be set (regardless of the actual setting); this is the key point in understanding the above.

But what if the words in your set already contain characters which have a special meaning in pattern matching, command substitution and other expansions? The remedy is easy: Just use something like

[[ "$WORD" == @('a*'|abc|'?ghi'|'$DontExpandMe'|barbaz) ]] && echo Word is in set.

I found this to be a very easy, understandable and efficient (as long as $WORD is short) solution which does not have side effects or is dangerous like the set-based methods.

2

You could define a bash function for this:

function word_valid() 
{ 
  if [ "$1" != "cat" -a "$1" != "dog" -a "$1" != "horse" ];then
    echo no
  else
    echo yes
  fi
}

Then use simply like this:

word_valid cat
1

i was searching for a 'one line' solution to validate my script argument, and used Joseph R. answer above to come up with:

[[ "$ARG" =~ ^(true|false)$ ]] || { echo "Argument received invalid value" ; exit 1 ; }

1

You could use fgrep to specify all allowed words:

if $(echo "$WORD" | fgrep -wq -e dog -e cat -e horse) ; then 
    echo yes
else
    echo no
fi

The -w flag matches only full words, the -q flag makes it operate silently (because all we need is the return value for the if statement to use), and each -e pattern specifies a pattern to allow.

fgrep is the version of grep that does normal string matching instead of regex matching. If you have grep, you should have fgrep, but if not, it's identical to using grep with the -F flag (so you would just replace fgrep -wq above with grep -Fwq).

0

This worked for me:

#!/bin/bash
phrase=(cat dog parrot cow horse)
findthis=parrot

for item in ${phrase[*]}
do
    test "$item" == "$findthis" && { echo "$findthis found!"; break; }
done
0

You may want to put the list of words into a file, in case you change the list often, or you want it to be shared by multiple scripts.  And you may need to put the words into a file if the list gets too long to manage in a script.  Then you can say

if fgrep –qx "$WORD" word_list
1
  • 1
    no, I don't want to have my list in a file – Martin Vegter Jan 30 '14 at 9:30
0

If words are a list where values are separated by a newline, you can do:

WORDS="$(ls -1)"
if echo "${WORDS}" | grep --quiet --line-regexp --fixed-strings "${WORD}"; then
    echo yes
else
    echo no
fi
0

For this purpose associate arrays together with test -v (checks if variable exists) are a good solution when you need an exact match:

#!/bin/bash
# declare associative array with all animals and no value
declare -A ANIMALS=(["dog"]= ["cat"]= ["horse"]=)

test_animal() {
  # check if parameter is found within the associative array
  if [[ -v ANIMALS[$1] ]] ; then
    echo "$1 is an animal"
  else
    echo "$1 is NOT an animal"
  fi
}

test_animal "sun"
test_animal "tree"
test_animal "dog"
test_animal "cat"
test_animal "horse"

Output:

sun is NOT an animal
tree is NOT an animal
dog is an animal
cat is an animal
horse is an animal

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.