我正在尝试使用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
([[:alnum:]]+)
匹配
正则表达式。.*?
匹配。([^/]+)
匹配。^
括起来(这里显然是不必要的)
和$
。如果没有$
,该表达式将不会输出我期望的结果。|
而不是/
作为分隔符,以避免将其转义到文件路径中。该命令返回:
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)
答案 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
不太像perl
(no non-greedy wildcards),所以这里有一个sed
的示例,它显示了我讨论的行为。
$ sed 's|^\([[:alnum:]]\+\).*/\([^/]\+\)$|\2 \1|' DCIM.md5
这基本上是perl
表达式除了的“直接翻译”,用于'/'
之前的多余[^/]
。我希望它能帮助比较sed
和perl
的人。
答案 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