用perl替换正则表达式EOL会产生意想不到的结果

时间:2018-03-01 08:24:09

标签: regex linux perl

为什么在第2行和第3行的开头有一个美元符号?

➜ echo -e "hello\nworld" | perl -pe 's/$/\$/g'
hello$
$world$
$%

上面,我试图在每一行的末尾添加一个美元符号,但不知怎的,它也在开头附加一个美元符号。启用全局标志时会执行此操作。但是当我删除全局标志时,它可以正常工作:

➜ echo -e "hello\nworld" | perl -pe 's/$/\$/'
hello$
world$

任何人都可以解释发生了什么吗?也许它与' \ r \ n'有关。字符?

编辑:添加lookbehind案例

这不仅仅是在这种情况下打破,还有其他情况。请考虑以下事项:

➜ echo -e "A\nB\nC\nD" | perl -pe 's/(?<!A)$/\$/'
A
$B$
C$
D$

上面,我想标记不会以&#34; A&#34;用$。 第2行的额外美元符号不应该存在。我甚至没有使用全球旗帜。

解决方案:现在好了。第二个解决方案是这样的(有关解释,请参阅Wiktor Stribiżew's answer

➜ echo -e "A\nB\nC\nD" | perl -pe 's/(?<!A|\n)$/\$/'
A
B$
C$
D$

但是要小心,如果您尝试使用多个单个字符,它会抛出 Variable length lookbehind not implemented in regex。例如:

➜ echo -e "AA\nBB\nCC\nDD" | perl -pe 's/(?<!AA|\n)$/\$/'
Variable length lookbehind not implemented in regex m/(?<!AA|\n)$/ at -e line 1.

要解决此问题,请在换行前添加适当数量的.

➜ echo -e "AA\nBB\nCC\nDD" | perl -pe 's/(?<!AA|.\n)$/\$/'
AA
BB$
CC$
DD$

2 个答案:

答案 0 :(得分:3)

关键是$是零宽度断言,它可以在最终换行符之前匹配。 Perl读取一行尾随\n,因此$匹配两次:之前和之后。

你的字符串基本上是Perl的两行:

hello\n
world\n

$可以在最终换行符之前和字符串的最后匹配。因此,两行中都有两个匹配(&#34;字符串&#34;在此上下文中)。

如果您想匹配字符串的最后一部分,请使用\z

perl -pe 's/\z/\$/g'

因为\z只匹配字符串的 very 结尾,但是不太可能有人想要使用它,因为它会有效地在<{1}}插入< em>开始第二行和后续行,并将其添加为最后一行。

要仅在最后$之前插入$并停止,请使用\n,不使用perl -pe 's/$/\$/'修饰符。

答案 1 :(得分:0)

如果您真的想将它与全局替换一起使用,可以使用以下命令:

echo -e "hello\nworld" | perl -pe 's/^(.*)$/\1\$/g'                                                                                          
hello$
world$

或没有反向引用,您可以使用:

echo -e "hello\nworld" | perl -pe 's/\n$/\$\n/g'
hello$
world$

如果您从Windows操作文件或仅使用\n删除Windows EOL字符\r\n,则可能需要将dos2unix替换为\r