Regular expressions in a Bash case statement

RegexBashCaseGlob

Regex Problem Overview


I am using following script, which uses case statement to find the server.

    #!/bin/bash
SERVER=$1;
echo $SERVER | egrep "ws-[0-9]+\.host\.com";
case $SERVER in
ws-[0-9]+\.host\.com) echo "Web Server"
;;
db-[0-9]+\.host\.com) echo "DB server"
;;
bk-[0-9]+\.host\.com) echo "Backup server"
;;
*)echo "Unknown server"
;;
esac

But it is not working. Regex is working with egrep but not with case. sample O/P

./test-back.sh ws-23.host.com
ws-23.host.com
Unknown server

Any Idea ?

Regex Solutions


Solution 1 - Regex

Bash case does not use regular expressions, but http://www.gnu.org/software/bash/manual/bashref.html#Pattern-Matching">shell pattern matching only.

Therefore, instead of regex ws-[0-9]+\.host\.com you should use pattern ws*.host.com (or ws-+([0-9]).host.com, but that looks a bit advanced and I've never tried that :-)

Solution 2 - Regex

If you want assert that * really matches digits in ws*.host.com and want to use case instead of if, elif, elif... you can use something like that:

case $SERVER in
  ws-[0123456789][0123456789][0123456789].host.com) echo "Web Server" ;;
  db-[0123456789][0123456789][0123456789].host.com) echo "DB server" ;;
  bk-[0123456789][0123456789][0123456789].host.com) echo "Backup server" ;;
  *) echo "Unknown server" ;;
esac

But that does not work for more than 999 servers.

If I had to make a script for this use case, I probably write something like that (because I love regexes and case syntax ;) ):

srv=`expr "$SERVER" : '^\(db\|bk\|ws\)-[0-9]\+\.host\.com$'`
echo -n "$SERVER : "
case $srv in
  ws) echo "Web Server" ;;
  db) echo "DB server" ;;
  bk) echo "Backup server" ;;
  *) echo "Unknown server !!!"
esac

Solution 3 - Regex

case can only use globs. If you want to do regex matching then you'll need to use a series of if-then-else-elif-fi statements, with [[.

Solution 4 - Regex

For reference, however this is already mentioned in this answer, from man bash Pattern Matching section provide rules for composite pattern creation as:

>Composite patterns may be formed using one or more of the following sub-patterns:
>
>?(pattern-list)
>        Matches zero or one occurrence of the given patterns.
>*(pattern-list)
>        Matches zero or more occurrences of the given patterns.
>+(pattern-list)
>        Matches one or more occurrences of the given patterns.
>@(pattern-list)
>        Matches one of the given patterns.
>!(pattern-list)
>        Matches anything except one of the given patterns.

However using these extended pattern matching require extglob shell option to be enabled.

Here is example of code for current problem:

shopt -s extglob;
SERVER="ws-45454.host.com";
case $SERVER in
        ws-+([0-9])\.host\.com) echo "Web Server"
                ;;
        db-+([0-9])\.host\.com) echo "DB server"
                ;;
        bk-+([0-9])\.host\.com) echo "Backup server"
                ;;
        *)echo "Unknown server"
                ;;
esac;
shopt -u extglob;

also, this: shopt | grep extglob can be used to check for its default value.

Solution 5 - Regex

Here’s an example of how to use the elif construct.

#!/bin/bash
SERVER=$1;
regex_ws="^ws-[0-9]+\.host\.com$"
regex_db="^db-[0-9]+\.host\.com$"
regex_bk="^bk-[0-9]+\.host\.com$"
if [[ "${SERVER}" =~ $regex_ws ]]; then
  echo "Web Server"
elif [[ "${SERVER}" =~ $regex_db ]]; then
  echo "DB server"
elif [[ "${SERVER}" =~ $regex_bk ]]; then
  echo "Backup server"
else
  echo "Unknown server"
fi

I find it most reliable to store the regular expressions in their own variables.

Solution 6 - Regex

I know this is a rather old question and my solution isn't much different from what @syjust has already provided, but I wanted to show that you can do just about anything at the matching stage in a case/esac statement.

$ cat case.sh && echo -e "#################\n" && bash case.sh ws-23.host.com
#!/bin/bash
SERVER=$1;
echo $SERVER | egrep "ws-[0-9]+\.host\.com";
case $SERVER in
  $(awk '{a=0}/ws-[0-9]*.host.com/{a=1}a' <<<${SERVER}))echo "Web Server";;
  $(awk '{a=0}/db-[0-9]*.host.com/{a=1}a' <<<${SERVER}))echo "DB Server";;
  $(awk '{a=0}/bk-[0-9]*.host.com/{a=1}a' <<<${SERVER}))echo "Backup Server";;
  *)echo "Unknown server";;
esac

#################

ws-23.host.com
Web Server

Solution 7 - Regex

You can also use expr to do the matching; it provides a grep-like regular expression syntax that should be robust enough for this application.

#!/bin/bash

server=$1
if   expr "$server" : 'ws-[0-9]\+\.host\.com' >/dev/null; then echo "Web server"
elif expr "$server" : 'db-[0-9]\+\.host\.com' >/dev/null; then echo "DB server"
elif expr "$server" : 'bk-[0-9]\+\.host\.com' >/dev/null; then echo "Backup server"
else echo "Unknown server"
fi

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUnniView Question on Stackoverflow
Solution 1 - RegexcheView Answer on Stackoverflow
Solution 2 - RegexsyjustView Answer on Stackoverflow
Solution 3 - RegexIgnacio Vazquez-AbramsView Answer on Stackoverflow
Solution 4 - Regexdinesh sainiView Answer on Stackoverflow
Solution 5 - RegexmlowryView Answer on Stackoverflow
Solution 6 - RegexAnthonyKView Answer on Stackoverflow
Solution 7 - RegextkocmathlaView Answer on Stackoverflow