为什么这两个命令的输出不同?
cat config.xml|perl -ne 'print $1,"\n" if /([0-9\.]+):161/'
cat config.xml|perl -ne "print $1,"\n" if /([0-9\.]+):161/"
首先按预期打印匹配组,而秒打印整行。
我发现您的命令有两个主要问题。
首先,双引号允许 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 /.../
这意味着 that\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(无用的 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/