【问题标题】:Bash check if CIDR address is validBash 检查 CIDR 地址是否有效
【发布时间】:2018-10-09 14:06:16
【问题描述】:

我似乎无法理解这一点。 我有一个正则表达式来检查字符串是否包含有效的 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 [ "$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

【问题讨论】:

  • =~ 使用扩展正则表达式,而不是与 Perl 兼容的正则表达式 (PCRE)。
  • 如果你有 GNU grep,你也许可以使用带有 if echo $1|grep -Pq $REGEX 的原始正则表达式。如果您需要使用 POSIX 正则表达式,则非捕获组不可用 ((?:...)),前瞻 ((?!...)) 也不可用。
  • 您可以使用(((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]?))(\/([1-9]|[1-2][0-9]|3[0-2]))([^0-9.]|$)
  • 谢谢大家。 Matt Raines 解决方案适用于 grep。 Revo 的解决方案适用于 =~ 和 grep。

标签: regex bash validation cidr


【解决方案1】:

通往成功的最短路径是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/29127.0.0.0/8127.3.0.0/10192.168.248.0/21

【讨论】:

  • 我明白你的意思。我正在使用的输入来自 whois 查询,所以我认为它是有效的 CIDR。最终输出为:insert into blacklist.cidrlist(cidr, minip, maxip, minint, maxint, created) values('${CIDR}','${MINIP}','${MAXIP}', inet_aton('${MINIP}'), inet_aton('${MAXIP}'), now());
  • 如果您不需要验证 CIDR 范围,那么您可能也不需要验证 IP 地址?如果您从文件流中提取 CIDR 范围,请考虑 grep -Po '(\d+\.){3}\d+/\d+'。我确信它不会包含误报,而且它肯定更适合一行。 :-)
【解决方案2】:

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”,则该函数将失败。

【讨论】:

  • 优秀。我喜欢这样我可以调整它以准确注销 CIDR 输入的问题。
  • 优秀的替代品!我觉得这个解决方案是特定于 bash 的,而我上面的解决方案是 POSIX sh。并不是说它有很大的不同,但值得注意的是,两个答案中都没有#!... 行,因此读者可能会遇到意外错误。也就是说,对于@ghoti 的回答,请使用#!/bin/bash#!/usr/bin/bash#!/usr/local/bin/bash,具体取决于您的操作系统,对于我的解决方案,请使用它或#!/bin/sh
猜你喜欢
  • 2013-06-16
  • 2014-04-03
  • 2019-10-26
  • 1970-01-01
  • 2021-07-30
  • 1970-01-01
  • 2011-10-25
  • 1970-01-01
  • 2013-05-03
相关资源
最近更新 更多