regex Bash检查CIDR地址是否有效

wbgh16ku  于 2023-04-07  发布在  其他
关注(0)|答案(3)|浏览(110)

我有一个正则表达式可以检查一个字符串是否包含有效的CIDR表示法地址。
(((?: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])
这个东西可以在Perl,PHP,Javascript中工作,并将x.x.x.x/8匹配到y.y.y.y/32
我试着把这些\d改为[[:digit:]]\\d Nothing:(
用于测试的测试脚本:

#!/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

有什么想法从这里去哪里?
已更新。添加了工作脚本:

#!/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
2j4z5cfb

2j4z5cfb1#

成功的最短路径是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使其静默,并依赖于退出代码来确定成功。
但是我应该指出,你的正则表达式并不完全匹配某个东西是一个有效的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/24127.1.0.0127.1.1.0/24等测试。
或更多奇数范围:10.10.10.8/29127.0.0.0/8127.3.0.0/10192.168.248.0/21

xurqigkl

xurqigkl2#

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”,则会失败。

fykwrbwg

fykwrbwg3#

对不起,我只是第一次开始编写脚本,想了解一切。这当然不是最优雅和最短的方式,但作为一个网络家伙,我理解每一步,我能够在每一步启用调试。

#!/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 "%08d\n" "$binOct1")
  binOct2=$(printf "%08d\n" "$binOct2")
  binOct3=$(printf "%08d\n" "$binOct3")
  binOct4=$(printf "%08d\n" "$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

相关问题