我似乎无法解决这个问题。 我有一个正则表达式,可以检查字符串是否包含有效的 CIDR 表示法地址。
(((?:25[012345]|2[0-4]d|1?dd?).){3}(?:25[012345]|2[0-4]d|1?dd?))(?:/([1-9]|[1-2][0-9]|3[0-2]))(?![.d])
这个东西可以在Perl,PHP,Javascript中工作,并将x.x.x.x/8
与y.y.y.y/32
匹配。
我试图将这些d
更改为[[:digit:]]
并\d
什么都没有:(
用于测试的测试脚本:
#!/bin/bash
if [ "$1" = "" ]
then
echo "Usage: $( basename $0) 123.456.789.0/12"
exit
fi
REGEX1='(((?:25[012345]|2[0-4]d|1?dd?).){3}(?:25[012345]|2[0-4]d|1?dd?))(?:/([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
有什么想法要从这里去哪里吗?
更新。添加了工作脚本:
#!/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
通往成功的最短路径是GNU grep,它也支持PCRE:
#!/bin/sh
if echo "$CIDR" | grep -qP "$REGEX"
then
echo "$CIDR OK!"
exit 0
else
echo "$CIDR NOT OK!"
exit 1
fi
Grep 的-q
使其保持沉默,并依靠退出代码来确定成功。 -P
是PCRE。
但我应该指出,你的正则表达式并不完全匹配某些东西是一个有效的CIDR范围;相反,你匹配了一个有效的IP地址,后跟一个斜杠和一个数字n∈1-32。CIDR 范围的另一个要求是地址的 32-n 低位为零,例如:
#!/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
}
测试这一点,例如 127.0.0.0/24
、127.1.0.0
、127.1.1.0/24
。
或更奇数的范围:10.10.10.8/29
、127.0.0.0/8
、127.3.0.0/10
、192.168.248.0/21
。
Simon的解决方案很优雅。 :)
我不太喜欢复杂的正则表达式来验证应该以其他方式解释的意义,所以或者,如果你更喜欢使用字符串操作而不是数学来做到这一点,我写了以下函数不久前:
valid_cidr_network() {
local ip="${1%/*}" # strip bits to leave ip address
local bits="${1#*/}" # strip ip address to leave bits
local IFS=.; local -a a=($ip)
# Sanity checks (only simple regexes)
[[ $ip =~ ^[0-9]+(.[0-9]+){3}$ ]] || return 1
[[ $bits =~ ^[0-9]+$ ]] || return 1
[[ $bits -gt 32 ]] || return 1
# Create an array of 8-digit binary numbers from 0 to 255
local -a binary=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
local binip=""
# Test and append values of quads
for quad in {0..3}; do
[[ "${a[$quad]}" -gt 255 ]] && return 1
printf -v binip '%s%s' "$binip" "${binary[${a[$quad]}]}"
done
# Fail if any bits are set in the host portion
[[ ${binip:$bits} = *1* ]] && return 1
return 0
}
此函数以二进制形式组装 IP 地址,如果在 IP 地址的主机部分中设置了任何"1",则失败。
#!/bin/bash
function checkCidrFormat {
local ipCidr="${1}"
local validIpCidr
validIpCidr='(^([1-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]).([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]).([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]).([0-9]|[1-9][0-9]|[1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5])/([1-9]|[1-2][0-9]|[3][0-2]))$'
if [[ $ipCidr =~ ^$validIpCidr ]]; then
echo "valid format"
return 0
else
echo "not valid format"
return 1
fi
}
function checkCidrValid {
local ip
ip=${1%/*}
local netBits
netBits=${1#*/}
#split IP to octets
local oct1
oct1=$(echo "${ip}" | tr "." " " | awk '{ print $1 }')
local oct2
oct2=$(echo "${ip}" | tr "." " " | awk '{ print $2 }')
local oct3
oct3=$(echo "${ip}" | tr "." " " | awk '{ print $3 }')
local oct4
oct4=$(echo "${ip}" | tr "." " " | awk '{ print $4 }')
#convert octets to binary
local binOct1
binOct1=$(echo "obase=2;$oct1" | bc)
local binOct2
binOct2=$(echo "obase=2;$oct2" | bc)
local binOct3
binOct3=$(echo "obase=2;$oct3" | bc)
local binOct4
binOct4=$(echo "obase=2;$oct4" | bc)
#fill leading zeros
binOct1=$(printf "%08dn" "$binOct1")
binOct2=$(printf "%08dn" "$binOct2")
binOct3=$(printf "%08dn" "$binOct3")
binOct4=$(printf "%08dn" "$binOct4")
#concat all binary octets
local binIp
binIp="${binOct1}${binOct2}${binOct3}${binOct4}"
#create binary mask - fill net bits
local binMask
for (( c=1; c<="${netBits}"; c++ ))
do
binMask+=1
done
#create binary mask - fill host bits
local hostBits
hostBits=$((32 - "${netBits}"))
for (( c=1; c<="${hostBits}"; c++ ))
do
binMask+=0
done
#show host bits from ip vs host bits from mask
local hostBitsIp
hostBitsIp="${binIp: -${hostBits}}"
local hostBitsMask
hostBitsMask="${binMask: -${hostBits}}"
#check if given ip was a valid network id, a broadcast or host
if [[ "${hostBitsIp}" =~ ^[1]+$ ]]; then
echo "its a broadcast address!"
return 1
elif [[ "${hostBitsIp}" =~ ^[0]+$ ]]; then
echo "its a valid network id for this cidr!"
return 0
else
echo "its a host ip!"
return 1
fi
}
while true;
do
read -rp "ip cidr (eg. 172.16.16.32/27): " cidr
if checkCidrFormat "${cidr}"; then
checkCidrValid "${cidr}"
fi
done