Regular expressions in a Bash case statement

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 ?


Bash case does not use regular expressions, but 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 :-)


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

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 [[.


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.