62

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 ?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Unni
  • 1,721
  • 2
  • 14
  • 12

7 Answers7

66

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 :-)

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
che
  • 12,097
  • 7
  • 42
  • 71
  • 1
    Thank you @glenn_jackman. On some machines (CentOS 7.3 with bash 4.2.46(1)) I was getting syntax errors "-bash: syntax error near unexpected token `('" for pattern +([a-zA-Z0-9])=*. On a fc19 bash version 4.2.53(1) machine no syntax error - extglob was set by default. – gaoithe May 18 '17 at 08:49
  • 1
    @gaoithe Thanks for this comment! I had a +() construction in a case statement, and interactively it worked but in a bash script the syntax was refused. I didn't understand it until I found your comment. When I switched on extglob in my script, the problem disappeared. – Onnonymous Sep 26 '21 at 07:36
18

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
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
syjust
  • 327
  • 2
  • 4
17

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.

dinesh saini
  • 431
  • 5
  • 8
14

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

codeforester
  • 39,467
  • 16
  • 112
  • 140
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
8

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

AnthonyK
  • 561
  • 5
  • 12
  • This is a nice idea, but why not just `$(awk '/ws-[0-9]*.host.com/' <<< ${SERVER})`? There does not seem to be a need for the `{a=0}` and `{a=1}a` – William Pursell Aug 30 '22 at 12:51
  • I most likely copied that from another script I used - and now that I think of it, I have no clue why I even have it in that one. Bad habits I guess. Thanks for pointing it out. It doesn't change the end result, other than wasted keystrokes - 33 of them - :(! – AnthonyK Sep 01 '22 at 07:26
7

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.

mlowry
  • 161
  • 5
  • 12
5

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
tkocmathla
  • 901
  • 11
  • 24