0

The string I'm trying to match is an ip address and I've seens quite some examples on the web. However it appears that I'm missing something which may or may not have to do with the differences between regexp conventions. (pcre, ere, ??)

To break it down to the point I'm missing here's the partial ip address that I'm trying to get matched:

ip="255.255."

This is a regular expression that tries to match above ip:

^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){2}

It's bash so I've got this to compare:

[[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){2} ]] && echo "ok"

Alas, it doesn't match.

As far as I know, I'm allowed to escape the . because I want it to match a point character, not just any character. (what . stands for in regexp to the best of my knowledge.)

This is what happens when I remove the escape before the point to illustrate above statement:

# ip="255.255."
# [[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?.){2} ]] && echo "ok"
ok
# ip="255X255Y"
# [[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9] [0-9]?.){2} ]] && echo "ok"
ok

That's not correct to match (partial) ip in this case.

Why does it not match this:

# ip="255.255."
# [[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){2} ]] && echo "ok" || echo "nope"
nope

There are two groups and each of them is "255." and that part matches just fine like this:

# ip="255."
# [[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.) ]] && echo "ok" || echo "nope"
ok  

EDIT: some extra info that possibly solves my issue:

just noticed the following:

# ip="172.15.11.10"
# [[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){2} ]] && echo "ok" || echo "nope"
ok
# ip="172.15."
# [[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){2} ]] && echo "ok" || echo "nope"
ok

And when the following grouping is made:

# [[ ${ip} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){2} ]] && echo "ok" || echo "nope"
nope
# [[ ${ip} =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){2} ]] && echo "ok" || echo "nope"
ok

So grouping the "number" part in front of the seperator "point" solves the issue?

# ip="255.255."
# [[ ${ip} =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){2} ]] && echo "ok" || echo "nope"
ok

I'm inclined to think this solves it however I do not yet understand why.

EDIT: the full regexp for the ip address is this here:

^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)

This is the same mentioned by Jeff in below answer.

1 Answer 1

3

The parenthesis were in the wrong place.

In the failing regex, you had grouped the octet's possibilities together with alternations:

^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.)

... which would match the beginning of line ^ followed by:

  • 25[0-5] or
  • 2[0-4][0-9] or
  • [01]?[0-9][0-9]?\.

Notice how the period is part of the 3rd possible alternation. That forces the regex to match the initial 255, leaving the following period unmatched.

You want the octet and the period to be repeated, so group the regex like this:

^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){2}

or like this:

^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

... so that there are four octets.

That forces the IP address to appear by itself on a line. If you don't care where in the line the IP address shows up, then drop the leading (^) and trailing ($) anchors.

On Linux, for a visual aid in testing, you could use grep --color=always -E ..., for instance:

$ ip=jeff-255.255.255.255-foo
$ echo "$ip" | grep --color=always -E '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
jeff-255.255.255.255-foo

... where the 255.255.255.255 shows up in color.

1
  • thanks for the explanation, so the grouping was wrong and this made "\." become a part of the last possible alteration from the list. it works great now. Also thanks for the coloring option, I never looked at that before. :-)
    – lievendp
    Commented Nov 21, 2018 at 15:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .