【问题标题】:Multi platform script perl or awk多平台脚本 perl 或 awk
【发布时间】:2012-10-02 14:51:23
【问题描述】:

我正在尝试匹配以下格式的记录:

(-,username,domain1.co.uk)\
(-,username,domain2.co.uk)

必须使用 awk 或 perl。我正在使用 cygwin 并编写了以下代码,该代码有效并与上述两个条目匹配:

awk 'BEGIN {musr="(-,username,[^)]+.co.uk)"} {if ($0~musr) print $0}' netgroup

但如果我尝试将这个正则表达式修改为更具体,则输出什么都没有:

第一个:匹配记录然后最后一个反斜杠然后匹配换行符:

"(-,username,[^)]+.co.uk)\\$"

第二个:记录后立即匹配新行,不带反斜杠:

"(-,username,[^)]+.co.uk)$"

所以我决定将脚本重写为 perl,希望 perl 可以处理反斜杠和行尾符号。为此,我以这种方式使用 a2p:

echo  'BEGIN {musr="(-,username,[^)]+.co.uk)"} {if ($0~musr) print $0}' | a2p.exe 
#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
                        # this emulates #! processing on NIH machines.
                        # (remove #! line above if indigestible)

eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_0-9]+=)(.*)/ && shift;
                        # process any FOO=bar switches

$, = ' ';               # set output field separator
$\ = "\n";              # set output record separator

$musr = '(-,username,[^)]+.co.uk)';

while (<>) {
    chomp;      # strip record separator
    if ($_ =~ $musr) {
        print $_;
    }
}

这个生成的 perl 脚本也匹配这两个条目,但是如果我尝试将这个脚本修改为更具体的,我会收到以下错误:

第一个:

$musr = "(-,username,[^)]+.co.uk)\\";
Trailing \ in regex m/(-,username,[^)]+.co.uk)\/ at perlmatch.pl line 18, <> line 1.

第二个:

$musr = "(-,username,[^)]+.co.uk)$";
Final $ should be \$ or $name at perlmatch.pl line 14, within string
syntax error at perlmatch.pl line 14, near "= "(-,username,[^)]+.co.uk)$""
Execution of perlmatch.pl aborted due to compilation errors.

第三:

$musr = "(-,username,[^)]+.co.uk)\$";
[the output is nothing]

我做错了什么?我的问题还指出,如果有人需要在多个平台(aix、solaris、linux)上使用脚本而不是使用 perl 应该是处理(非)GNU utils 和各种(g|n)awk 版本等更好的方法。问候

【问题讨论】:

    标签: regex perl awk backslash eol


    【解决方案1】:

    您的问题来自 Perl 中的字符串引用。

    $musr = "(-,username,[^)]+.co.uk)\\"; 在创建字符串时用单个反斜杠替换 \\。但是您需要将两个反斜杠传递给正则表达式。所以你必须在创建字符串时输入四个。

    $musr = "(-,username,[^)]+.co.uk)$"; 尝试在字符串中执行变量插值。

    此外,如 John Kugelman 所述,括号应该被转义。

    解决方案是对正则表达式使用 Perl 的内置分隔符,而不是普通的带引号的字符串。简单的方法是将其直接放入您的循环中:

    while (<>) {
        chomp;      # strip record separator
        if ($_ =~ /\(-,username,[^)]+.co.uk\)$/) {
            print $_;
        }
    }
    

    如果您确实需要先将模式放入变量中,请使用特殊的qr// 运算符。

    my $musr = qr/\(-,username,[^)]+.co.uk\)$/;
    while (<>) {
        chomp;      # strip record separator
        if ($_ =~ $musr) {
            print $_;
        }
    }
    

    【讨论】:

    • 谢谢 qr 操作员是我一直在寻找的东西。
    • 当然也可以转义括号。还有一个问题,为什么应该有 qr 并且简单地引用正则表达式是不够的?问候
    • @Wakan Tanka,正则表达式使用特殊语法,其中某些字符和某些转义码具有特殊含义。因此,Perl 为您提供了特殊的qr// 运算符来很好地处理这个问题。如果必须将其放入常规字符串中,则需要进行两层转义:一层用于字符串,另一层用于正则表达式。这造成了难以理解的混乱。
    【解决方案2】:
    (-,username,[^)]+.co.uk)\\$
    

    这里的问题不在于行尾的反斜杠,而在于括号。括号用于分组。您需要对它们进行转义以匹配文字 ( ) 字符。您还应该转义点,以便它们匹配文字点而不是“任何字符”。

    $ awk '/\(-,username,[^)]+\.co\.uk\)$/   {print}' netgroup 
    (-,username,domain2.co.uk)
    $ awk '/\(-,username,[^)]+\.co\.uk\)\\$/ {print}' netgroup 
    (-,username,domain1.co.uk)\
    

    如果您坚持使用普通的 awk 并且不使用 [gn]awk 特有的功能,那么 awk 是非常便携的。我认为比 perl 更便携。

    【讨论】:

    • Awk 和 Perl 都可以在任何主要平台上使用。我不认为一个比另一个“更便携”。这取决于你想做什么。
    • 坚持使用普通 awk 是什么意思?有时我需要在多个平台(AIX、Solaris、HP-UX、Linux、Cygwin)之间编写脚本,而我的经验是“经典”linux 命令(awk、sed)在所有 *UNIX 中的行为略有不同。例如。 Solaris 中的 sed 不能像 linux 那样简单地用换行符替换任何字符。 (sed 's/char/\n/') 等。我只是 perl 新手,但到目前为止,当我编写 perl 代码时,它可以在所有平台上运行,没有任何问题,不像 [gn]awk sed 等。问候
    【解决方案3】:

    括号必须被转义。否则,它们将表达式分组。更具体地说,在行尾匹配一个可选的反斜杠(反斜杠是双倍的,因为它们作为字符串也必须被转义)。

    awk 'BEGIN {musr="\\(-,username,[^)]+.co.uk\\)\\\\?$"} {if ($0~musr) print $0}' netgroup
    

    【讨论】:

    • 多谢回复,已经逃出来的让我抓狂了,能不能发点好资源?
    猜你喜欢
    • 2018-03-09
    • 1970-01-01
    • 2011-02-24
    • 2015-11-08
    • 2015-06-01
    • 2012-05-30
    • 2016-11-22
    • 2015-04-25
    • 1970-01-01
    相关资源
    最近更新 更多