为什么这个单引号和双引号在输出上产生如此大的差异

时间:2013-08-21 10:03:30

标签: regex perl shell

为什么这两个命令的输出不同?

cat config.xml|perl -ne 'print $1,"\n" if /([0-9\.]+):161/'

cat config.xml|perl -ne "print $1,"\n" if /([0-9\.]+):161/"

首先按预期打印出匹配组,而秒打印整行。

3 个答案:

答案 0 :(得分:5)

我发现你的命令有两个主要问题。

首先,双引号允许shell插值,$1将用于shell变量并替换。由于它不太可能存在,它将被替换为空字符串。因此,print $1代替print,它是print $_的简写,可能是整行打印的原因。

其次,你的命令中有未转义的双引号,所以你实际上是将三个字符串传递给Perl:

print ,
\n
 if /(....)/

至于为什么或如何使用你的shell,我不知道,因为我无法访问你的操作系统,也不知道它是哪一个。在Windows中,我收到nUnquoted string "n" may clash with future reserved word at -e line 1.)的Perl裸字警告,这意味着\n被解释为字符串。现在,这是棘手的部分。我们得到的是:

print , \n if /.../

这意味着\n不再是print的参数,它是print之后的语句,它位于void上下文中,因此会被忽略。我们可以通过这个警告(我必须在我的shell中伪造)看到这个:

Useless use of single ref constructor in void context at -e line 1.

(请注意,由于您不使用警告,因此不会收到这些警告 - -w开关)

所以我们留下的是

print if /.../

这正是您所描述的行为的代码:它在找到匹配项时打印整行。

您可以做的是将shell中的问题可视化,将-MO=Deparse开关添加到您的单行中,如下所示:

C:\perl>perl -MO=Deparse -ne"print ,"\n" if /a/"
LINE: while (defined($_ = <ARGV>)) {
    print($_), \'n' if /a/;
}
-e syntax OK

现在我们可以清楚地看到print语句与换行符分开,并且换行符是对字符串的引用。


<强>解决方案:

但是,您的代码还有其他问题,如果做得好,您可以避免所有shell困难。首先,你有一个UUOC (Useless Use of Cat)。在命令行上使用-n开关时,可以为perl指定文件参数。其次,您不需要为此使用变量,只需打印正则表达式的返回值:

perl -nlwe 'print for /(...)/' config.xml

-l开关将为您处理换行符,并在这种情况下为打印添加换行符。 for是避免打印空匹配所必需的。

答案 1 :(得分:3)

在双引号内,一些东西被替换($ variable,`command`,..)。在单引号内,它们保持不变。

$ echo "$HOME"
/home/falsetru
$ echo '$HOME'
$HOME

$ echo "`echo 1`"
1
$ echo '`echo 1`'
`echo 1`

嵌套引号:

$ echo ""hello""
hello
$ echo '"hello"'
"hello"
$ echo "\"hello\""
"hello"

转义双引号,$以获得相同的结果:

cat config.xml | perl -ne "print \$1,\"\n\" if /([0-9\.]+):161/"

答案 2 :(得分:2)

两件事:

  1. 嵌套引号。
  2. 变量以不同方式展开。
  3. 第一个命令有一个恰好包含一些双引号的字符串。变量展开。

    第二个命令有两个字符串,其间有一个不带引号的\n。变量 已扩展。

    我们说$1包含“blah”

    第一个将此字符串传递给perl:

    print $1,"\n" if /([0-9\.]+):161/
    

    第二个,这个:

    print blah,\n if /([0-9\.]+):161/