【问题标题】:Checking correctness of an email address with a regular expression in Bash在 Bash 中使用正则表达式检查电子邮件地址的正确性
【发布时间】:2011-01-09 11:16:23
【问题描述】:

我正在尝试制作一个 Bash 脚本来检查电子邮件地址是否正确。

我有这个正则表达式:

[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?

来源:http://www.regular-expressions.info/email.html

这是我的 bash 脚本:

regex=[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?

i="test@terra.es"
if [[ $i=~$regex ]] ; then
    echo "OK"
else
    echo "not OK"
fi

脚本失败并给我这个输出:

10:语法错误:反引号替换中的 EOF

有什么线索吗??

【问题讨论】:

  • 您知道国际化域名icann.org/en/topics/idn 吗?你的正则表达式匹配 test@fõõ.bâr.com 吗?
  • 如果你仔细阅读你引用的那篇文章,你会发现 a) 正则表达式只会帮助你整理出明显非法的地址,b) 你要么有误报和漏报,要么完全笨拙的正则表达式,并且 c) 最后,您必须实际向该地址发送一封电子邮件,以检查它是否不仅在语法上有效,而且实际上是否正确(没有正则表达式可以告诉您)。

标签: regex bash shell email-validation


【解决方案1】:

这里有几个问题:

  • 正则表达式需要加引号,特殊字符需要转义。
  • 应该锚定正则表达式(^$)。
  • ?: 不受支持,需要删除。
  • =~ 运算符周围需要空格。

最终产品:

regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"

i="test@terra.es"
if [[ $i =~ $regex ]] ; then
    echo "OK"
else
    echo "not OK"
fi

【讨论】:

  • 我想补充一点,但是,在 bash 中执行此操作是非常不理想的。但我想强调如何解决您选择的方法。
  • 当我在 Ubuntu 13 中测试但在 Ubuntu 12 中通过时,此正则表达式失败
  • 这个正则表达式对我有用:regex="^[a-zA-Z0-9\._-]+\@[a-zA-Z0-9._-]+\.[ a-zA-Z]+\$"
  • 请注意,@PeterEisentraut 的回答不适用于包含仍然有效的大写字符的电子邮件地址。
【解决方案2】:

您不必创建如此复杂的正则表达式来检查有效的电子邮件。您可以简单地在“@”上拆分,然后检查是否有 2 个项目,一个在 @ 前面,另一个在后面。

i="test@terraes"
IFS="@"
set -- $i
if [ "${#@}" -ne 2 ];then
    echo "invalid email"
fi
domain="$2"
dig $domain | grep "ANSWER: 0" 1>/dev/null && echo "domain not ok"

要进一步检查域,您可以使用 dig 等工具来查询域。它比正则表达式更好,因为 @new.jersey 可以被正则表达式匹配,但它实际上不是正确的域。

【讨论】:

  • 我实际上认为这是一种更明智的方法。想想所有不允许的网站:test+marker@domain.com,即使这是一封完全有效的电子邮件。它应该摆脱大多数假货并且仍然可以。您可以通过检查“。”的存在来使其更强大。在第二个元素中,并确保它将第二个元素分隔为 2 个子元素。以国际域名为例
  • @Jean:第二个元素也可能包含两个以上用点分隔的子字符串,所以这很好,尽管在某些情况下您可能希望允许邮件 ligḱe user@localhost 跨度>
【解决方案3】:

引号、反引号和其他字符是 shell 脚本中的特殊字符,如果像在 regex 的赋值中那样使用它们,则需要对其进行转义。您可以使用反斜杠转义特殊字符,或者如果省略其中使用的单引号,则在正则表达式周围使用单引号。

我建议使用像.*@.* 这样更简单的正则表达式,因为所有的复杂性都是徒劳的。 foo@example.com 看起来非常好,可以被任何正则表达式接受,但它仍然不存在。

【讨论】:

    【解决方案4】:

    低于 3.2 的 Bash 版本:

    if [[ "$email" =~ "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$" ]]
    then
        echo "Email address $email is valid."
    else
        echo "Email address $email is invalid."
    fi
    

    Bash version greater than or equal to 3.2:

    if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$ ]]
    then
        echo "Email address $email is valid."
    else
        echo "Email address $email is invalid."
    fi
    

    here 解释了您不应使用非常具体的正则表达式的原因,如您所拥有的。

    【讨论】:

    【解决方案5】:

    您的脚本的直接问题是您需要修复引用:

    regex='[a-z0-9!#$%&'"'"'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'"'"'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?'
    

    但是,此正则表达式不接受所有语法上有效的电子邮件地址。即便如此,并非所有语法上有效的电子邮件地址都可以送达。

    如果您关心的是可交付地址,那么就不要使用正则表达式或其他检查语法的方法:向用户提供的地址发送质询。 注意不要在命令调用中使用不受信任的输入! 使用 sendmail,运行 sendmail -oi -t 并将消息写入 sendmail 进程的标准输入,例如

    收件人:test@terra.es.invalid
    来自:no-reply@your.organization.invalid
    主题:电子邮件地址确认
    
    要确认您的地址,请访问以下链接:
    
    http://www.your.organization.invalid/verify/1a456fadef213443
    

    【讨论】:

      【解决方案6】:

      有一次疯狂的时候,我根据精通正则表达式这本书写了这个 Perl 子例程:

      sub getRFC822AddressSpec
      {
          my ($esc, $space, $tab, $period) = ('\\\\', '\040', '\t', '\.');
          my ($lBr, $rBr, $lPa, $rPa)      = ('\[', '\]', '\(', '\)');
          my ($nonAscii, $ctrl, $CRlist)   = ('\200-\377', '\000-\037', '\n\015');
      
          my $qtext       = qq{ [^$esc$nonAscii$CRlist] }; # within "..."
          my $dtext       = qq{ [^$esc$nonAscii$CRlist$lBr$rBr] }; # within [...]
          my $ctext       = qq{ [^$esc$nonAscii$CRlist()] }; # within (...)
          my $quoted_pair = qq{ $esc [^$nonAscii] }; # an escaped char
          my $atom_char   = qq{ [^()$space<>\@,;:".$esc$lBr$rBr$ctrl$nonAscii] };
          my $atom        = qq{ $atom_char+     # some atom chars
                                (?!$atom_char)  # NOT followed by part of an atom
                              };
          # rfc822 comments are (enclosed (in parentheses) like this)
          my $cNested     = qq{ $lPa (?: $ctext | $quoted_pair )* $rPa };
          my $comment     = qq{ $lPa (?: $ctext | $quoted_pair | $cNested )* $rPa };
      
          # whitespace and comments may be scattered liberally
          my $X           = qq{ (?: [$space$tab] | $comment )* };
      
          my $quoted_str  = qq{ " (?: $qtext | $quoted_pair )* " };
          my $word        = qq{ (?: $atom | $quoted_str ) };
          my $domain_ref  = $atom;
          my $domain_lit  = qq{ $lBr (?: $dtext | $quoted_pair )* $rBr };
          my $sub_domain  = qq{ (?: $domain_ref | $domain_lit ) };
          my $domain      = qq{ $sub_domain (?: $X $period $X $sub_domain )* };
          my $local_part  = qq{ $word (?: $X $period $X $word )* };
          my $addr_spec   = qq{ $local_part $X \@ $X $domain };
      
          # return a regular expression object
          return qr{$addr_spec}ox;
      }
      
      my $spec = getRFC822AddressSpec();
      my $address = q{foo (Mr. John Foo) @ bar. example};
      print "$address is an email address" if ($address =~ qr{$spec});
      

      【讨论】:

        【解决方案7】:

        我已经调整了上面的示例,使其具有一个独特的功能,该功能将使用正则表达式检查地址的有效性,如果域实际存在,则使用 dig,否则返回错误。

        #!/bin/bash
        #Regexp
        regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"
        
        #Vars
        checkdig=0;
        checkreg=0;
        address=$1;
        maildomain=`echo $address | awk 'BEGIN { FS = "@" } ; { print $2 }'`;
        
        #Domain Check
        checkdns() {
                echo $maildomain | awk 'BEGIN { FS = "@" } ; { print $2 }' | xargs dig $maildomain | grep "ANSWER: 0" 1>/dev/null  || checkdig=1;
        }
        
        #Regexp
        checkreg() {
                if [[ $address =~ $regex ]] ;
                        then checkreg=1;
                fi
        }
        
        #Execute
        checkreg;
        checkdns;
        
        #Results
        if [ $checkreg == 1 ] && [ $checkdig == 1 ];
                then    echo "OK";
                else    echo "not OK";
        fi
        #End
        

        没什么特别的。

        【讨论】:

          【解决方案8】:

          聚会迟到了,但我修改了一个脚本来读取包含电子邮件的文件,并使用 RFC822 正则表达式、域错字列表、mx 查找(感谢此处的 eagle1)和模棱两可的电子邮件过滤对其进行过滤。

          脚本可以这样使用:

          ./emailCheck.sh /path/to/emailList
          

          并生成两个文件,过滤列表和模糊列表。两者都已从不符合 RFC822 的地址、没有有效 MX 域的电子邮件域和域拼写错误中清除。

          脚本可以在这里找到:https://github.com/deajan/linuxscripts

          欢迎指正和cmets :)

          【讨论】:

            猜你喜欢
            • 2017-06-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-08-09
            相关资源
            最近更新 更多