我有一个文件列表:

./a.temp.txt     ./a.temp.txt
./a/b.temp.txt   ./a/b.temp.txt
./a/b/c.temp.txt ./a/b/c.temp.txt


我想删除每行上的temp.,但是仅第二次出现,因此该文件应如下所示:

./a.temp.txt     ./a.txt
./a/b.temp.txt   ./a/b.txt
./a/b/c.temp.txt ./a/b/c.txt


我应该怎么做?

评论

是否可能在任何一行上出现第三或第四次出现?这些必须保持完好吗?

我的情况是将旧文件名与新文件名匹配,因此每行仅出现2次。而且只有第二个应该更改。

#1 楼

通常,您可以使用\zs\{N}匹配某事的第N次出现。在:help \zs处有一个示例。

您的情况下的命令是:

:%s/\(.\{-}\zstemp\.\)\{2}//


评论


使用verymagic标记,可以减少正则表达式:%s / \ v(。{-} \ zstemp。){2} //

– nobe4
16年7月8日在6:40

#2 楼

使用sed可以更轻松地完成此操作:

sed 's/\.temp\././2'


使用Vim,您需要非贪婪匹配,并且很难将方法扩展为替换第3,第4个等发生temp。但是只要您坚持就可以做到:

:%s/\.temp\..\{-}\.\zstemp\.//


评论


感谢您提供sed解决方案,您认为可以在每行中应用它吗?

– nobe4
16年7月7日在11:28

@ nobe4不知道那是什么意思。 sed依次将脚本应用于输入的每一行。

–佐藤桂
16年7月7日在11:30

是的,但是您可以将行传递到外部程序中并返回结果。

– nobe4
16年7月7日在11:32

你是说Vim吗?当然,您可以通过管道传输sed,就像您可以通过管道将其传输到任何其他程序一样。 F.i .::%!sed's / \。temp \ ././ 2'。以UNIX友好的sed格式,它从stdin读取输入并将结果写入stdout。

–佐藤桂
16年7月7日在11:35

#3 楼

您也可以回过头来执行此操作。

:%s/\(temp\..*\)\@<=temp\.//


如果要删除第一次出现的所有事件,可以在末尾附加g标志。

\(\)\@<=将搜索\(\)之间的任何模式,但不会将找到的文本添加到匹配项中。

有关更多信息,请参见:h \@<=

#4 楼

您可以使用已经建议的@Meshpi宏。但是,这是完成同一件事的另一种方法。

02/temp^Mdwx+



0-转到行的开头

2 / temp-搜索temp并转到第二个出现的位置

^ M-只需按Enter键

dw-删除单词(temp)

x-删除'。'

+-转到下一行的开头


然后,您可以重复直到最后。并且,将有更多的方法可以做到这一点。

#5 楼

此解决方案与TessellatingHeckler的解决方案类似,但更易于适应必须删除的任何模式。

br />
:g/temp\./对于与“温度”匹配的每一行。

normal在正常模式下执行以下操作



2n找到模式的第二次出现

gn选择它

d并将其删除。



这将适用于任何情况模式被赋予:g
normal可以缩写为norm
gnd等同于dgn
编辑:实际上2ngnd等同于d2gn

请参见Vim Tips Wiki提供了有关全局命令的更多示例。

#6 楼

在Vim中,可以定义要与:help \%c进行匹配的之前/之后/之后的列:

:%s/\%>17c\.temp/g


这将消除在列之后找到的每个.temp当前缓冲区中每行的17。


注1:您的示例不需要/g

注2:我非常方便地使用了该功能经常与qmv一起使用。推荐!

评论


整洁,我当时正在考虑制作一个插件,但这很好!

– nobe4
16年7月7日在12:35

Vim已经做了很多事情……

– romainl
16年7月7日在12:37

可以,但是我不知道这个工具。一点脚本总是很有趣!

– nobe4
16年7月7日在12:38



@ nobe4如果您关心这种事情,请参见moreutils软件包中的vidir和vipe。

–佐藤桂
16年7月7日在13:17

#7 楼

这实际上很简单。要匹配第二个温度,允许在其后加上“ temp”,然后再寻找温度。

%s/.*temp.*\zstemp\.


\zs的意思是“从此处开始选择”,因此比赛的第一部分(第二温度之前的所有内容)不会被删除。实际上,这是我刚刚学到的极其有用的功能!没有它,则正则表达式必须如下所示:

:%s/\(.*temp.*\)temp\./


评论


如果一行上有两个以上的温度,则您的配方会删除最后一个而不是第二个。责怪贪婪。 :)

–lcd047
16年7月7日在20:13

是的,这很贪婪,因此它删除了最后一个。但是,OP表示我的情况是将旧文件名与新文件名匹配,因此每行仅出现2次。而且只有第二个应该改变。

–詹姆斯
16年7月7日在20:15

#8 楼

代替正则表达式,我将执行以下命令:


跳转到行尾
向后搜索temp

删除5个字符

并在每一行上运行它:

:g//normal $?temp^[5x


NB。 ^[是特殊的,表示按Escape键;您可以使用以下命令键入它:Ctrl-q <Esc>


但是正则表达式选项(我认为还没有建议)是匹配temp.,后跟除.之外的其他任何内容,并固定到该行。

:%s/temp\.\([^.]\+\)$//


将与行尾的temp.txt匹配,并仅用txt位替换。

#9 楼

我将用光标从左上角开始写一个宏。
qq4f.dfpq

然后我将使用V选择其余的行,并使用:'<,'>norm!@q在这些行上运行宏

仅当文本格式相对于第二个温度之前的点数保持相同时,此方法才有效。

评论


若要使您的宏更健壮,应使用搜索和n而不是4f。因为如果文件名中包含更多的点,您的宏可能会失败或删除不应删除的内容。

–statox♦
16年7月7日在11:28

#10 楼

没有什么可以阻止您使用两个正则表达式而不是一个,即更改第一个正则表达式然后交换列:

可读性强。

#11 楼

我的尝试

:%norm 4ftdwx

:%norm ......... execute in normal mode over the whole file  
4ft ............ the 4th t matches the start of the second temp
dw ............. deletes the current word
x .............. deletes the dot