regexp贪婪:缩小长路

时间:2012-11-11 20:09:25

标签: regex perl shell path sed

请看看我的思想破碎者。

我坚持用正则表达式缩小了一些长路径,就像这样:

/12345/123456/1234/123/12/1/1234567/13245678/123456789/1234567890

我想将此路径转换为以下形式:

/123/123/123/123/12/1/123/123/123/123

路径中的每个“目录”仅缩写为3个第一个字符

LONG_PATH="/12345/123456/1234/123/12/1/1234567/13245678/123456789/1234567890"
perl -pe "s#/(.{1,3})[^/]*?(/|$)#/\1\2#g" <<<$LONG_PATH

/ 123 /123分之123456/12分之123//132分之1234567/123分之123456789

sed -E "s#/(.{1,3})[^/]*?(/|$)#/\1\2#g" <<<$LONG_PATH

/ 123 /123分之123456/12分之123//132分之1234567/123分之123456789

我也试过了:

perl -pe "s,/(.)(.)?(.)?[^/]*+,/\1\2\3,g" <<<$LONG_PATH
/123/123/123/123/12//123/132/123/123

还有很多,没有“运气” - 我仍然不知道。

请指出正确的成功之道。

3 个答案:

答案 0 :(得分:7)

最多匹配三个非斜杠字符并捕获它们。然后匹配其余的直到下一个斜线。替换为捕获:

"s#(/[^/]{3})[^/]*#\1#g"

此处不需要不明确或任何内容,因为否定的字符类与/$互斥。

编辑虽然您似乎知道这一点,但我可能会为将来的访问者澄清这可以与perl -pe...sed -E...一起使用,因为您在问题中使用了它。正则表达式也可以与sed -r...一样使用。如果省略-E-r选项,那么(像往常一样)您将需要转义括号和大括号:

sed "s#\(/[^/]\{3\}\)[^/]*#\1#g" filename

另请注意,ikegami指出,在Perl中,您应该在替换中使用$1而不是\1

答案 1 :(得分:3)

你可以这样做:

perl -pe's#[^/]{3}\K[^/]*##g'
/12345/123456/1234/123/12/1/1234567/13245678/123456789/1234567890
/123/123/123/123/12/1/123/132/123/123

找到3个非斜杠,并保留(\K)它们,删除下面的字符直到下一个斜杠。

正如ikegami所指出的,不需要匹配少于三个字符,在这种情况下,可以使用lookbehind断言而不是\K。好处是\K需要perl v5.10,而且我相信环绕式断言早于此。

perl -pe 's#(?<=[^/]{3})[^/]*##g'

答案 2 :(得分:0)

最好的方法似乎是使用File::Spec模块来拆分和重新组合路径。对map的中间调用会将每个路径段减少为前三个字符。该计划演示

use strict;
use warnings;

use File::Spec;

my $path = '/12345/123456/1234/123/12/1/1234567/13245678/123456789/1234567890';

my $newpath = File::Spec->catdir(map substr($_, 0, 3), File::Spec->splitdir($path));

print $newpath;

<强>输出

/123/123/123/123/12/1/123/132/123/123