s ///返回原处的换行符

时间:2018-09-15 17:47:09

标签: regex perl newline

我正在尝试使用Perl重新排序md5文件的内容。对于每一行,我希望文件名不包含路径,然后再包含哈希。我想出的最好的命令是:

$ perl -pe 's|^([[:alnum:]]+).*?([^/]+)$|$2 $1|' DCIM.md5

输入文件(DCIM.md5)由md5sum在Linux上生成。看起来像这样:

e26ff03dc1bac80226e200c0c63d17a2  ./Path1/IMG_20150201_160548.jpg
01f92572e4c6f2ea42bd904497e4f939  ./Path 2/IMG_20150204_190528.jpg
afce027c977944188b4f97c5dd1bd101  ./Path3/Path 4/IMG_20151011_193008.jpg
  1. 哈希与
    中的第一组([[:alnum:]]+)匹配 正则表达式。
  2. 然后文件的空格和路径是
    .*?匹配。
  3. 然后,文件名由([^/]+)匹配。
  4. 表达式用^括起来(这里显然是不必要的) 和$。如果没有$,该表达式将不会输出我期望的结果。
  5. 我使用|而不是/作为分隔符,以避免将其转义到文件路径中。

该命令返回:

IMG_20150201_160548.jpg
 e26ff03dc1bac80226e200c0c63d17a2IMG_20150204_190528.jpg
 01f92572e4c6f2ea42bd904497e4f939IMG_20151011_193008.jpg
 afce027c977944188b4f97c5dd1bd101IMG_20151011_195133.jpg

匹配正确,输出顺序正确(文件名不带路径,然后进行哈希处理),但间距不正确:文件名后有换行符。我希望它在哈希之后,像这样:

IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101

在我看来,我的命令输出换行符,但我不知道如何更改此行为。 或者问题可能出在外壳上,而不是命令上?

最后,一些版本信息:

$ perl -version
This is perl 5, version 22, subversion 1 (v5.22.1) built for i686-linux-gnu-thread-multi-64int
(with 69 registered patches, see perl -V for more detail)

4 个答案:

答案 0 :(得分:5)

[^/]+匹配换行符,因此您输入的内容是$2的一部分,它首先放在转换后的$_中($1中没有换行符,因此$_的末尾没有换行符。)

解决方案:从perlrun阅读-l选项。特别是:

  

-l [octnum]   启用自动行尾处理。它有两个单独的作用。首先,当与-n或-p一起使用时,它会自动砍掉$ /(输入记录分隔符)。其次,它将$ \(输出记录分隔符)分配为octnum的值,以便任何打印语句将重新添加该分隔符。如果省略octnum,则将$ \设置为$ /的当前值。

答案 1 :(得分:3)

替代解决方案,它使用了其他答案和注释中的许多概念...

$ perl -pe 's|(\p{hex}+).*?([^/]+?)$|$2 $1|' DCIM.md5

...和解释。

调查所有答案并尝试找出答案后,我认为问题的根源是[^/]+贪婪。它的贪婪使它捕获了换行符。它会忽略$锚点。

这让我很难理解,因为在使用Perl之前我已经使用sed进行了很多解析,即使是贪婪的通配符也无法在sed中捕获换行符。希望这篇文章能对那些(像我一样习惯sed)也想知道(像我一样)为什么$的行为不像“我期望的那样”的人有所帮助。

通过尝试我将发布的内容作为另一个替代答案,我们可以看到“贪婪”问题。

写文件:

$ cat > DCIM.md5<<EOF
> e26ff03dc1bac80226e200c0c63d17a2  ./Path1/IMG_20150201_160548.jpg
> 01f92572e4c6f2ea42bd904497e4f939  ./Path 2/IMG_20150204_190528.jpg
> afce027c977944188b4f97c5dd1bd101  ./Path3/Path 4/IMG_20151011_193008.jpg
> EOF

通过将贪婪[^/]+更改为[^/]+?来摆脱它。解析。

$ perl -pe 's|([[:alnum:]]+).*?([^/]+?)$|$2 $1|' DCIM.md5
IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101

所需的输出已完成。

@Shawn的accepted answer

$ perl -lpe 's|^([[:alnum:]]+).*?([^/]+)$|$2 $1|' DCIM.md5

基本上更改$锚,以使其表现为sed人所期望的行为。

@CrafterKolyan的answer照顾贪婪的[^/]捕获换行符,说您不能使用正斜杠换行符。这个答案仍然需要$锚点来防止出现以下情况

1).*捕获空字符串( 0 或任意多个字符)

2)[^/\n]+捕获 .

@Borodin的答案采用了截然不同的方法,但这是一个很棒的概念。

@Borodin做出了出色的comment,它使该答案的版本更精确/更精确,这是我在本文顶部放置的版本。

最后,如果要遵循Perl programming model,这是另一种选择。

$ perl -pe 's|([[:xdigit:]]+).*?([^/]+?)(\n\|\Z)|$2 $1$3|' DCIM.md5

P.S。因为sed不太像perlno non-greedy wildcards),所以这里有一个sed的示例,它显示了我讨论的行为。

$ sed 's|^\([[:alnum:]]\+\).*/\([^/]\+\)$|\2 \1|' DCIM.md5

这基本上是perl表达式除了的“直接翻译”,用于'/'之前的多余[^/]。我希望它能帮助比较sedperl的人。

答案 2 :(得分:2)

使用[^/\n]代替[^/]

perl -pe 's|^([[:alnum:]]+).*?([^/\n]+)$|$2 $1|' DCIM.md5

答案 3 :(得分:2)

进行替换使您不得不编写一个正则表达式模式,该模式与您不需要想要的所有内容以及您要做的所有事情相匹配。通常,最好只匹配您需要的部分并从中构建另一个字符串

for ( <> ) {
    die unless m< (\w++) .*? ([^/\s]+) \s* \z >x;
    print "$2 $1\n";
}

或者如果您必须有单线纸

perl -ne 'die unless m< (\w++) .*? ([^/\s]+) \s*\z >x; print "$2 $1\n";' myfile.md5

输出

IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101