2017-08-14 40 views
1

如果我想从字符串中删除第一个句点及其后面的所有内容,在sed中我可以例如这样做:替换第n个正则表达式的问题

echo 2.6.0.3-8 | sed 's/\..*//' 

输出:

2 

但是,如果我想删除第二个时期,一切背后,我想我应该可以做这样的(GNU SED):

echo 2.6.0.3-8 | sed 's/\..*//2g' 

然而输出为:

2.6.0.3-8 

从手册:

'NUMBER' 只有更换REGEXP的NUMBERth比赛。

我在这里错过了什么?

+1

你错过了它是贪婪的。 '。*'是在吞噬一切,没有第二个匹配。你可以使用'[^。] *'效果好于'。*' – stevesliva

回答

2

你有,但通过.*和贪婪烫伤更精确。所有你必须为你的具体情况做是[^.]*替换.*

 
$ echo 2.6.0.3-8 | sed 's/\.[^.]*//2g' 
2.6 
$ echo 2.6.0.3-8 | sed 's/\.[^.]*//3g' 
2.6.0 
$ echo 2.6.0.3-8 | sed 's/\.[^.]*//1g' 
2 

[^.]意味着不是一个点的所有字符。

+0

+1,我看到并用'g'取代了其余的行。请注意,这使得它成为GNU特定的sed。你知道sed的便携式解决方案吗? – Thor

+0

我相信格伦的便携式。 '-E'更多的是POSIX,然后是GNU sed的man页面提供的'-r',但'-E'可以用于GNU sed。 – stevesliva

+0

是的你是对的。但是他并没有使用我想工作的/// n'表格。我发现了一个有点骇人听闻的方式,看到我的回答 – Thor

2

这是因为表达是贪婪的。第一场比赛消耗.6.0.3-8,第二场比赛没有剩余文字。

你必须与你的正则表达式

$ sed -E 's/([^.]+(\.[^.]+){3}).*/\1/' <<<"2.6.0.3-8" 
2.6.0.3-8 
$ sed -E 's/([^.]+(\.[^.]+){2}).*/\1/' <<<"2.6.0.3-8" 
2.6.0 
$ sed -E 's/([^.]+(\.[^.]+){1}).*/\1/' <<<"2.6.0.3-8" 
2.6 
$ sed -E 's/([^.]+(\.[^.]+){0}).*/\1/' <<<"2.6.0.3-8" 
2 
+0

忽略我以前的评论,这很好,很好的答案。但是,它并没有像我在这个问题中所做的那样使用/// n'形式。 – Thor

+0

请参阅@史蒂夫的答案如何使用GNU sed和我的一个稍微黑客但便携的方式做 – Thor

1

正如@stevesliva和@glennjackman所指出的,这里的问题是正则表达式匹配整行,所以没有第二个匹配。

似乎没有一种通用的方法来实现用正则表达式替换。因此,通用的替代方案,消除了第二期,一切的背后是用Pd,如:

echo 2.6.0.3-8 | sed 's/\./\n/2; P; d' 

或者便携:在这两种情况下

echo 2.6.0.3-8 | sed -e $'s/\\./\\\n/2' -e P -e d 

输出:

2.6 
+0

啊,现在我明白了。我没有把注意力集中在一个与'g'相关的数字上,这是未定义的行为。那就是说,分号应该是可移植的?我在OSX上用BSD sed用户看到的通常的可移植性问题是,在花括号内必须有最终分号。 – stevesliva

+0

@stevesliva:的确我也这么认为,但是使用分号分隔时Debian的busybox sed的上述失败 – Thor

+0

我猜这是busybox在将其传递给busybox sed之前解开单引号字符串的更多问题。即:'$ echo's/\ ./ \ n/2'| sed -f-'cause'sed:file-line 1:unterminated s'command' while'$ echo's/\\ ./ \\\ n/2'| sed -f-'没有语法错误。这基本上是一个外壳扩展问题。除非您允许两次字符串扩展,否则Sed不会看到正确的命令字符串。 – stevesliva