Bash 检查 CIDR 地址是否有效

Bash check if CIDR address is valid

我似乎无法理解这个问题。 我有一个正则表达式,用于检查字符串是否包含有效的 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/8y.y.y.y/32

我已经尝试将那些 \d 更改为 [[:digit:]]\d 没有:(

用于测试的测试脚本:

#!/bin/bash

if [ "" = "" ]
then
    echo "Usage: $( basename [=11=]) 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 [[  =~ $REGEX ]]
then
    echo " OK!"
else
    echo " Not OK! $REGEX"
fi

从这里开始有什么想法吗?

已更新。添加了工作脚本:

#!/bin/bash

if [ "" = "" ]
then
    echo "Usage: $( basename [=12=]) 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 [[  =~ $REGEX ]]
then
    echo " OK!"
else
    echo " Not OK!"
fi

if echo  | grep -Pq $REGEX
then
    echo "grep  OK!"
else
    echo "grep  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=""

  # 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/29127.0.0.0/8127.3.0.0/10192.168.248.0/21

西蒙的解决方案很优雅。 :)

我不太喜欢用复杂的正则表达式来验证那些应该用其他方式解释的有意义的东西,所以或者,如果你更喜欢用字符串操作而不是数学来做这件事,我写道不久前的以下功能:

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