Using BASH =~ regex to match multiple strings

I have a scripting problem that I'm trying to solve, whereby I want to match that a string contains either of three strings. I'm thinking this is probably just me not understanding how to craft the appropriate regex. However, here's what I would like to do:

[[ $STRING =~ "one|two|three" ]] && do-something

more specifically:

[[ $STRING =~ "one OR two OR three" ]] && do something || :

I'd like to be able to match based on whether it has one or more of those strings -- or possibly all.

I'm sure this is simple, I just can't get my brain around it.

I know that BASH =~ regex can be system-specific, based on the libs available -- in this case, this is primarily CentOS 6.x (some OSX Mavericks with Macports, but not needed)

Thanks!

Omit the quotes

[[ $STRING =~ one|two|three ]] && do-something

While I currently have no clue if it is advisable to quote "$STRING" .

You don't need to do so because neither word splitting nor pathname expansion(globbing) are performed in this context.

1 Like

When you quote the ERE in <STRING> =~ <ERE> , it's taken literally.

So am I to infer from the responses that what I am attempting to do cannot be done in this context within BASH?

FYI, this does not work:

[[ "$1" =~ "one|two|three" ]]

basically, I'm looking to generate a positive result if it matches any of those words.

I've seen other examples (in different context) that will do something:

[[ "$1" =~ (regex) ]]

but with the emphasis that "regex" is very much platform-dependent (ie: depends highly on the regular expressions libs available on that system.

I could do something more verbose like this, but it defeats the purpose of succinct code:

[[ "$1" =~ "one" ]] || [[ "$1" =~ "two" ]] || [[ "$1" =~ "three" ]] 

or more verbose using:

if [ "$1" =~ "one" ]
then
  do something
elseif [  "$1" =~ "two" ]
  do another-thing
elseif [ "$1" =~ "three" ]
  do that
else
  echo No match
fi

That's horrible to look at.

Also it would need to be (bash/ksh93/zsh):

[[ $STRING =~ ^(one|two|three)$ ]] && do-something

or

if [[ $STRING =~ ^(one|two|three)$ ]]; then
  do_something
fi

or, using a case statement

case $STRING in 
  one|two|three) do_something
esac

On CentOS 6.5, I am unable to get this to work correctly (per above):

[[ $STRING =~ ^(one|two|three)$ ]] && do-something

The string will actually be a part of a hostname, for example "one-hostname" or something like that. I need it check that there's a match, possibly doing something based on what it matched. But in this case the match word will be at the beginning ^one.

No, not at all...

That is a string match, not a regex match see other comments...

Where did you get that idea? That is not correct.. It does depend on bash version. Old versions of bash cannot process =~ , (but a case statement using unix patterns will work)

------------

What happens when you execute:

STRING=one; [[ $STRING =~ ^(one|two|three)$ ]] && echo hello

in a bash shell?

  • How do you execute your script?

@forrie,
consider the following:

$ echo $BASH_VERSION
4.1.10(4)-release
p='^(one|two|three)$'
$ for s in one two three "$p"; do
>   [[ $s =~ $p ]] && printf '%s matches %s\n' "$s" "$p"
>   [[ $s =~ "$p" ]] && printf '%s matches "%s"\n' "$s" "$p"
> done
one matches ^(one|two|three)$
two matches ^(one|two|three)$
three matches ^(one|two|three)$
^(one|two|three)$ matches "^(one|two|three)$"

As I said, when you quote the regular expression, it's taken literally.
As far as I know, the =~ operator is bash version specific
(i.e. it's not available in older bash versions).
There are some other gotchas and some platform specific issues, see the BashWiki for more info (see Portability Considerations).

I got this to work:

[[ $STRING =~ ^(one|two|three)$ ]] && echo hello

This also matches a hostname like

one-hostname

I'm running BASH

GNU bash, version 4.3.11(1)-release (x86_64-apple-darwin13.1.0)

The problem was user-related :slight_smile: I had our internal strings on the brain (dev, prod, test) and was under-caffeinated.

Thank you all for your helpful pointers.

If STRING is set to one-hostname , the command:

[[ $STRING =~ ^(one|two|three)$ ]] && echo hello

should not produce any output. If you're trying to match one , two , or three at the start of the string and accept anything following one of those three strings, you don't want the $ . With the $ , the RE matches only if one of those three strings is the entire contents of $STRING .