2

In my bash script I identify the machine name - kafka01 or kafka02 or kafka03 with the following regular expression bash code

if [[ $(hostname -s) =~ ^kafka[[:digit:]] ]]
then
/tmp/run.sh
fi

Example from hostname command:

hostname -s
kafka01

But now we want to run the script - /tmp/run.sh also if the machine name is mngkafka01 or mngkafka02 or mngkafka03.

So we did the following; this should run the script run.sh if machine name is kafka01 or mngkafka01, etc:

if [[ $(hostname -s) =~ ^[mng]kafka[[:digit:]] ]]
then
/tmp/run.sh
fi

But this regular syntax does not work.

What is wrong with my regular expression code?

9

Quasimodo already gave you the answer for why your regex was failing. I will suggest a different approach. Instead of regular expressions, use case and globs:

case $(hostname -s) in
  kafka[0-9]*|mngkafka[0-9]*)
    /tmp/run.sh
esac

This is much easier to extend to more patterns and, personally, I find it easier to read.

2
  • Nice alternative with the Bonus of running in any POSIX shell.
    – Quasímodo
    Nov 2 '20 at 18:09
  • 3
    Using uname -n instead of hostname -s would make it completely POSIX compliant. Note however that [0-9]* is not the same as regexp [[:digit:]]+$ for which you'd need non standard glob extentions like zsh -o extendedglob's [0-9]## or ksh/zsh -o kshglob/bash -o extglob's +([0-9]). Except in the C/POSIX locale, [0-9] may (and often do) not match the same thing as [[:digit:]] or [0123456789]. Nov 3 '20 at 8:07
7

[mng] does not match mng, it matches either m or n or g. An appropriate regex is (^|^mng)kafka[[:digit:]]+$. This matches

  • (^|^mng) either the null string or mng at the string start,
  • kafka,
  • [[:digit:]]+$ one or more digits anchored to the string end.

Notice that your previous regex would also raise a false positive for kafka7u as it was not anchored.

Test:

arr=(kafka01 kafka7u mngkafka01 gkafka7x)
for i in "${arr[@]}"; do
    if [[ $i =~ (^|^mng)kafka[[:digit:]]+$ ]]; then
        echo "$i"
    fi
done

Output:

kafka01
mngkafka01

More information: Conditional Constructs (see [[...]] section).

The fixed version to your original attempt:

if [[ $(hostname -s) =~ (^|^mng)kafka[[:digit:]]+$ ]]
then
    /tmp/run.sh
fi
5
  • Out of curiosity: Is there an advantage to stating (^|^mng) instead of ^(mng)??
    – AdminBee
    Nov 3 '20 at 9:43
  • @AdminBee No. ^(mng)? is more elegant, though, I only did not use it because I didn't remember I could use ? with a capture group. Thank you for reactivating a neural connection in my brain ;)
    – Quasímodo
    Nov 3 '20 at 12:42
  • You're welcome ;)
    – AdminBee
    Nov 3 '20 at 13:00
  • In my experience on Stack Overflow, this seems to be one of the most common errors of regex newbies. I've never gotten anyone to explain where they got the idea that [] is used for grouping instead of (), but it seems to be rampant.
    – Barmar
    Nov 3 '20 at 15:05
  • @Barmar Manual pages use [argument] for optional arguments. Maybe some of them get it from there, while others most likely simply give it a shot.
    – Quasímodo
    Nov 3 '20 at 18:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy