【问题标题】:Bash based regex domain name validation基于 Bash 的正则表达式域名验证
【发布时间】:2013-02-22 12:30:42
【问题描述】:

我想创建一个脚本,将新域添加到我们的 DNS 服务器。 我发现Fully qualified domain name validation REGEX。 但是,当我将它与 sed 一起使用时,它并没有像我预期的那样工作:

echo test | sed  '/(?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(:[a-zA-Z]{2,})$)/p'  
--------
Output is: 
test
echo test.com | sed  '/(?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(:[a-zA-Z]{2,})$)/p'  
--------
Output is: 
test.com

我希望第一个命令的输出应该是一个空行。 我做错了什么?

【问题讨论】:

  • 它根本不匹配您的字符串。尝试sed -n 确认。

标签: regex bash dns


【解决方案1】:

我发现这是一个更全面的正则表达式:

(?=^.{4,253}$)(^(?:[a-zA-Z0-9](?:(?:[a-zA-Z0-9\-]){0,61}[a-zA-Z0-9])?\.)+([a-zA-Z]{2,}|xn--[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$)

  • RFC 1034§3: Allows 长度为 4-253,我所知道的最短操作域“t.co”仍然与其他答案不匹配。 255 字节是最大长度,减去每个标签(TLD 和“主”子域)的长度八位字节后得到 253:@​​987654332@
    • RFC 3696§2:单字母 TLD在技术上是允许的,这意味着最小长度为 3,但由于目前没有单字母 TLD,最小长度为 4 是可行的。
  • RFC 1034§3:允许子域中的数字,而 Conor Clafferty 显然不允许(通过不将其他子域与“主要”子域 - 即您注册的域 - DNS 规范不区分)
  • RFC 1034§3:将单个标签限制为 63 个字符,允许中间使用连字符,同时将开头和结尾限制为字母数字 (?:[a-zA-Z0-9](?:(?:[a-zA-Z0-9\-]){,61}[a-zA-Z0-9])?\.)
  • 需要两个字母或更大的 TLD,但可能是 punycoded ([a-zA-Z]{2,}|xn--[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])
    • RFC 3696§2:DNS 规范在技术上允许 TLD 中使用数字,以及单字母 TLD;但是,目前没有单字母 TLD 或带数字的 TLD,并且不允许使用全数字 TLD,因此这部分正则表达式已简化为 [a-zA-Z]{2,}

      --或--

    • RFC 3490§5: 国际化域名 ccTLD (IDN c​​cTLD) 可能是 punycode,如“xn--”前缀所示,其后可能包含字母、数字或连字符。这近似于xn--[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]

      请注意,此模式不会验证 punycode TLD!将容忍无效的 punycode,例如“xn--qqqq”,因为尝试根据适当的编码机制验证 punycode 超出了正则表达式的范围。虽然 punycode 本身在技术上允许以连字符结尾的编码字符串,但RFC 3492§5 遵守并尊重标签不能以连字符结尾的 IDNA 限制。

编辑 02/2021:向 user2241415 致敬,指出 IDN c​​cTLD 与之前指定的正则表达式不匹配。

【讨论】:

  • 感谢您如此精确、解释自己并引用来源。有助于做出快速、明智的选择。
  • 对我来说就像一个魅力..我还编写了一个名为 isdom 的 bash 函数,所以我可以用'isdom string'调用它,它会根据这个正则表达式响应是/否..
  • 为什么不工作? echo example.com|grep -P '(?=^.{4,253}$)(^(?:[a-zA-Z](?:(?:[a-zA-Z0-9\-]){ ,61}[a-zA-Z])?\.)+[a-zA-Z]{2,}$)'
  • @roothahn 请查看我的编辑。显然 PCRE (heh) 的一些解释不喜欢隐式下限 ({,61}) 所以我添加了一个显式下限 ({0,61}) 并且它与 grep 一起玩得更好:echo example.com|grep -P '(?=^.{4,253}$)(^(?:[a-zA-Z](?:(?:[a-zA-Z0-9\-]){0,61}[a-zA-Z])?\.)+[a-zA-Z]{2,}$)'
  • 它似乎没有验证新的 TLD,例如 - test.xn--kpu716f(每 swcs.com.au/tld.htm
【解决方案2】:

如果域必须存在,您可以尝试:

$ cat test.sh
#!/bin/bash

for h in "bert" "ernie" "www.google.com"
do
    host $h 2>&1 > /dev/null
    if [ $? -eq 0 ]
    then
        echo "$h is a FQDN"
    else
        echo "$h is not a FQDN"
    fi
done

jalderman@mba:/tmp$ ./test.sh 
bert is not a FQDN
ernie is not a FQDN
www.google.com is a FQDN

【讨论】:

    【解决方案3】:

    我使用grep -P 来执行此操作。

    echo test | grep -P "^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$" 
    --------
    Output is: 
    
    echo www.test.com | grep -P "^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$"
    --------
    Output is: www.test.com
    

    【讨论】:

    • 嘿,这对我有用。你能解释一下这个正则表达式让我清楚地理解吗?
    【解决方案4】:

    没有sed 实现我知道支持您在该正则表达式中使用的各种 Perl 扩展。尝试使用 Perl 或 grep -Ppcregrep,或将正则表达式简化为 sed 可以处理的内容。这是一个快速而肮脏的改编,它将正则表达式拆分为三个不同正则表达式的脚本,并在某些内容不匹配(或匹配,在最中间的情况下)时拒绝。

    echo 'test' | sed -r '/^.{5,254}$/!d
        /^([^.]*\.)*[0-9]+\./d   # Seems incorrect; 112.com is valid
        /^([a-zA-Z0-9_\-]{1,63}\.?)+([a-zA-Z]{2,})$/!d'  # should disallow underscore
        # also, what's with the question mark after the literal dot?
    

    这也完全无法接受 IDNA 域(其中可以包含 TLD 中的破折号和数字等),所以我绝对不推荐这个,但希望它向您展示如何将这样的内容调整为 sed 如果你愿意。

    【讨论】:

      【解决方案5】:

      Pierre-Louis 的回答不太适合我。例如“小猫”被视为域名。 我添加了一个细微的调整,以确保域中至少有一个点。

      (?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+\.(?:[a-z]{2,})$)
      

      在它读取域的最后一部分之前有一个额外的\.

      【讨论】:

      • 开头不应该是(?=^.{4,254}$) 吗? “t.co”是一个有效的域(目前正在使用中!),并且只有 4 个字符长...
      • “修复”不正确。虚假点现在允许在 TLD 之前有两个连续的点。更好的解决方法是删除已经存在的文字点之后的问号(但这在技术上是不正确的;例如,dk 单独是一个有效的域名)。
      【解决方案6】:

      您的正则表达式中缺少问号:

      (?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)

      你可以测试你的正则表达式here

      你可以用 grep 做你想做的事:

      $ echo test.com | grep -P '(?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)'
      test.com
      $ echo test | grep -P '(?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)'
      $
      

      【讨论】:

      • 如果我测试了 test.-com,它通过了。那是无效的,对吧?
      • 对我不起作用.. 自己尝试一下:echo fireb | grep -P '(?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)'。它将返回:fireb。但它不是域名。另一个例子:echo berif_novp | grep -P '(?=^.{5,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)'。返回:berif_novp,但这也不是一个域。即使在 rubular.com 上尝试 它正在匹配不是域的字符串。
      • 这个正则表达式的问题在于它违反了关于域的某些规则: 1. 域不能有下划线; 2. 标签不能开始结束连字符(每个标签的第一个和最后一个字符必须是字母数字); 3. 标签可以完全是数字(TLD 除外……也许),所以(?!\d+\.) 不合适; 4. 主分组中\. 上的? 量词不正确,因为它允许没有句点的域