【发布时间】:2013-08-21 10:03:29
【问题描述】:
为什么这两个命令的输出不同?
cat config.xml|perl -ne 'print $1,"\n" if /([0-9\.]+):161/'
cat config.xml|perl -ne "print $1,"\n" if /([0-9\.]+):161/"
First 按预期打印匹配组,而 seconds 打印整行。
【问题讨论】:
为什么这两个命令的输出不同?
cat config.xml|perl -ne 'print $1,"\n" if /([0-9\.]+):161/'
cat config.xml|perl -ne "print $1,"\n" if /([0-9\.]+):161/"
First 按预期打印匹配组,而 seconds 打印整行。
【问题讨论】:
我发现你的命令有两个主要问题。
首先,双引号允许 shell 插值,$1 将被用作 shell 变量并被替换。由于它不太可能存在,它将被替换为空字符串。因此,您得到的不是print $1,而是print,它是print $_ 的简写,这可能就是打印整行的原因。
其次,您的命令中有未转义的双引号,因此您实际上是在将三个字符串传递给 Perl:
print ,
\n
if /(....)/
至于为什么或如何使用您的 shell,我不知道,因为我无法访问您的操作系统,也不知道它是哪一个。在 Windows 中,我收到 n (Unquoted string "n" may clash with future reserved word at -e line 1.) 的 Perl 裸字警告,这意味着 \n 被解释为字符串。现在,这是棘手的部分。我们得到的是这样的:
print , \n if /.../
这意味着\n 不再是print 的参数,它是print 之后的一个语句,并且它处于无效上下文中,因此它被忽略。我们可以通过这个警告看到这一点(我不得不在我的 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 是避免打印空匹配项所必需的。
【讨论】:
在双引号内,一些东西被替换($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/"
【讨论】:
两件事:
第一个命令有一个字符串,恰好包含一些双引号。变量没有展开。
第二个命令有两个字符串,中间有一个不带引号的\n。变量被扩展了。
假设$1 包含“blah”
第一个将这个字符串传递给perl:
print $1,"\n" if /([0-9\.]+):161/
第二个,这个:
print blah,\n if /([0-9\.]+):161/
【讨论】: