Issue
I just can't seem to wrap my head around this. I have a regex that check if a string contains a valid CIDR notation address.
(((?:25[012345]|2[0-4]\d|1?\d\d?)\.){3}(?:25[012345]|2[0-4]\d|1?\d\d?))(?:\/([1-9]|[1-2][0-9]|3[0-2]))(?![.\d])
This thing works in Perl, PHP, Javascript, and matches x.x.x.x/8
to y.y.y.y/32
.
I've tried to change those \d
to [[:digit:]]
and to \\d
Nothing :(
The test script used to test:
#!/bin/bash
if [ "$1" = "" ]
then
echo "Usage: $( basename $0) 123.456.789.0/12"
exit
fi
REGEX1='(((?:25[012345]|2[0-4]\d|1?\d\d?)\.){3}(?:25[012345]|2[0-4]\d|1?\d\d?))(?:\/([1-9]|[1-2][0-9]|3[0-2]))(?![.\d])'
REGEX2='(((?:25[012345]|2[0-4]\\d|1?\\d\\d?)\.){3}(?:25[012345]|2[0-4]\\d|1?\\d\\d?))(?:\\/([1-9]|[1-2][0-9]|3[0-2]))(?![.\\d])'
REGEX3='(((?:25[012345]|2[0-4][[:digit:]]|1?[[:digit:]][[:digit:]]?)\\.){3}(?:25[012345]|2[0-4][[:digit:]]|1?[[:digit:]][[:digit:]]?))(?:\\/([1-9]|[1-2][0-9]|3[0-2]))(?![.[[:digit:]]])'
REGEX=$REGEX3
if [[ $1 =~ $REGEX ]]
then
echo "$1 OK!"
else
echo "$1 Not OK! $REGEX"
fi
Any ideas to where to go from here?
Updated. Added working script:
#!/bin/bash
if [ "$1" = "" ]
then
echo "Usage: $( basename $0) 123.456.789.0/12"
exit
fi
REGEX='(((25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?))(\/([8-9]|[1-2][0-9]|3[0-2]))([^0-9.]|$)'
if [[ $1 =~ $REGEX ]]
then
echo "$1 OK!"
else
echo "$1 Not OK!"
fi
if echo $1 | grep -Pq $REGEX
then
echo "grep $1 OK!"
else
echo "grep $1 Not OK!"
fi
Solution
The shortest path to success is GNU grep, which also supports PCRE:
#!/bin/sh
if echo "$CIDR" | grep -qP "$REGEX"
then
echo "$CIDR OK!"
exit 0
else
echo "$CIDR NOT OK!"
exit 1
fi
grep's -q
makes it silent and relies on the exit code to determine success. -P
is PCRE.
But I should point out that your regex does not fully match that something is a valid CIDR range; rather, you're matching a valid IP address followed by a slash and a number n ∈ 1-32. An additional requirement to CIDR ranges is that the 32-n lower bits of the address are zero, e.g.:
#!/bin/sh
valid_cidr() {
CIDR="$1"
# Parse "a.b.c.d/n" into five separate variables
IFS="./" read -r ip1 ip2 ip3 ip4 N <<< "$CIDR"
# Convert IP address from quad notation to integer
ip=$(($ip1 * 256 ** 3 + $ip2 * 256 ** 2 + $ip3 * 256 + $ip4))
# Remove upper bits and check that all $N lower bits are 0
if [ $(($ip % 2**(32-$N))) = 0 ]
then
return 0 # CIDR OK!
else
return 1 # CIDR NOT OK!
fi
}
Test this with e.g. 127.0.0.0/24
, 127.1.0.0
, 127.1.1.0/24
.
Or more odd ranges: 10.10.10.8/29
, 127.0.0.0/8
, 127.3.0.0/10
, 192.168.248.0/21
.
Answered By - Simon Shine