1

I want to match a version number inside a case statement. The version number could look like this

1.12.0.32
1.12.0.32.1
2.10.0.30.1.2

and is stored inside a shell variable: version.

As I searched on the internet, it's little tricky to match a regex inside a case-statement. But, does anyone has an idea or could help on how it can be done inside the case-statement?

I tried the following, but it didn't work:

case "$version" in
    "([0-9]+\.*)+")
                  echo "Correct"
        ;;
                 *)
                  echo "Not Correct"
        ;;
esac
8
  • 6
    What exactly would be correct or incorrect? Jul 29 at 12:46
  • 2
    Yes but what would be the comparison, could you give an example? If you want to match the exact string why not just use that in your case statement? Jul 29 at 12:53
  • 6
    Or what makes a version number valid or not? Jul 29 at 12:54
  • 5
    In other words, are those three examples correct and anything else incorrect? Or is it the shape rather then the digits that are to be checked?
    – roaima
    Jul 29 at 18:48
  • 1
    What about 1 - is that a valid version? It would be better to give a proper definition; specification by example leaves too many unknowns, especially when there are no negative examples (i.e. no list of strings that shouldn't match). Jul 30 at 10:50
7

case only supports shell patterns, and not regexes. But Bash supports regexes out of the box with a different syntax.

If you mean "any number of decimal numbers separated by dots", and e.g. no trailing letters or tags, then the pattern ^[0-9]+(\.[0-9]+)*$ would perhaps be appropriate. ([0-9]+\.*)+ would also match strings with consecutive or trailing dots, like 1.2..3.. So:

#!/bin/bash
re='^[0-9]+(\.[0-9]+)*$';
if [[ $version =~ $re ]]; then
    echo "'$version' matches the pattern"
else
    echo "'$version' doesn't match"
fi

The regex says to look for any number of decimal digits ([0-9]+), followed by any number of groups ((·)*) consisting of a literal dot (\.) and any number of decimal digits ([0-9]+). ^ and $ lock the pattern to the start and end of the string, otherwise a matching substring would be enough for a match.

That would allow a version string without any dots, like 123. If you want to require at least two numbers and the dot, then use ^[0-9]+(\.[0-9]+)+$ (changing the last * to a +).

Note that depending on the locale, what [0-9] matches might be a bit surprising, it could include some weirder Unicode digits. Spelling out the allowed characters with [0123456789] would be stricter in that, but is more awkward and error-prone to write.

6

case matches against shell patterns, not regular expressions. So instead of a regex like ^[0-9]+(\.[0-9]+)*$ you can use:

case $version in
  '' | *[!0123456789.]* | .* | *. | *..* ) echo invalid;;
  * ) echo fine;;
esac

i.e. "invert" the matching sense, using standard shell patterns to detect invalid cases.

Note that the extglob bash option applies to matching in case statements. So if you have that set with shopt -s extglob, then those extended patterns like +(), *(), etc. gain their special meanings, and you could do a more regex-like positive match by using +([0123456789])*(.+([0123456789])) (shell patterns are already "anchored" to the beginning and end).

3
  • 1
    one could also use ksh-style extended globs if the extglob shell option is set I think? so something close to the OPs regex might be +([0-9])*(.+([0-9])) Jul 29 at 13:21
  • Ah, inverting the test is indeed a nice standard solution here. But those look like standard shell patterns, so no need for extglob there. Using steeldrivers's +([0-9])*(.+([0-9])) would need extglob though.
    – ilkkachu
    Jul 29 at 14:28
  • @ilkkachu I've edited trying to make that more clear. Please feel free to edit it further (I'm not familiar with the intricacies of bash).
    – rowboat
    Jul 29 at 14:57
4

If it doesn't have to be case and you allow the use of an external tool like grep, you can use an if statement which might provide more flexibility:

if grep -q -E '^[0-9]+(\.[0-9]+)*$' <<< "$version"
then
    printf "%s is a valid version\n" "$version"
else
    printf "%s is NOT a valid version\n" "$version"
fi

This will accept version strings that consist of

  • at least one field of one or more consecutive digits ([0-9]+), followed by
  • zero or more fields that start with a separating . (escaped as \. since a simple . in RegExes means "any character"), followed by one or more consecutive digits (again [0-9]+).

The exhaustive nature of the match is ensured by anchoring it to the start (^) and end ($) of the string, so that a mere sub-string match would not trigger the result.

0

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.