9

So I have a program (say, programA), that will give me an output, for example: yes, no, maybe, probably, possibly, impossible, banana.

I want to make a script that will do something, whatever it is, based on that output. Let's say I only need to account for yes, maybe and banana.

So far what I would do, is use case like so:

case $program_output in
  yes) echo "good word: $program_output" ;;
  maybe) echo "good word: $program_output" ;;
  banana) echo "good word: $program_output" ;;
  *) echo "bad word: $program_output" ;;
esac

But recently I was fiddling with the if statement, and found out I can do this faster like so:

if [[ "yesmaybebanana" =~ ${program_output} ]]; then
  echo "good word: ${program_output}"; else echo "bad word: ${program_output}";
fi

Is there any reason why I should not use the if statement for this? This is a case where $program_output cannot have spaces in it, and it's a limited list of words it can output.

AdminBee
  • 21,637
  • 21
  • 47
  • 71
Lhakryma DL
  • 181
  • 1
  • 4

3 Answers3

19

If you want to use bash's regular expression functions (which however are bash-specific and therefore not portable), you should at least use the proper "OR"-type operator to enforce exact match with one of the allowed words, as in

if [[ $program_output =~ ^(yes|maybe|banana)$ ]]
then
    echo "Good word: $program_output"
else
    echo "Bad word: $program_output"
fi

Notice that there must be no quotes around the regular expression, and that the word alternatives are enclosed between ^ and $ anchors to ensure that no sub-string can trigger a match (such as yessir which would otherwise be caught by the pattern yes).

AdminBee
  • 21,637
  • 21
  • 47
  • 71
18

The if version is not going to be as reliable as case here because it will catch all substrings of yesmaybebanana - it will match for b, bebana etc:

#!/usr/bin/env bash

program_output='bebana'

if [[ "yesmaybebanana" =~ ${program_output} ]]; then echo "good word: ${program_output}"; else echo "bad word: ${program_output}"; fi

Output:

good word: bebana

And it's not portable - try running it with dash:

$ dash ./a.sh
./a.sh: 6: ./a.sh: [[: not found
bad word: bebana

You can significantly simplify your case version by using multiple words in a single line:

case $program_output in
  yes | maybe | banana) echo "good word: $program_output" ;;
  *) echo "bad word: $program_output" ;;
esac
Arkadiusz Drabczyk
  • 25,049
  • 5
  • 53
  • 68
  • 1
    "because it will catch all substrings of `yesmaybebanana`" -- as well as all matching regular expressions. If `program_output` is `.`, `.*`, `beba(na){2}`, `(foo bar)?`, etc. those would also be interpreted as good words. – JoL Apr 30 '20 at 21:37
13

bash's extended patterns also work here:

if [[ $program_output == @(yes|maybe|banana) ]]
then
    echo "program output is one of yes, maybe, banana"
fi
AdminBee
  • 21,637
  • 21
  • 47
  • 71
glenn jackman
  • 84,176
  • 15
  • 116
  • 168