今天我开始使用vim。我对:g
和:%s
命令感到困惑。那么,:g
和:%s
命令有什么区别呢?
:g
是全称 global 的缩写,它可以对所有符合正则表达式的行执行命令:
:g/LinesThatMatchThisRegex/ExecuteThisCommand
例子:
:g/hello/d
这将删除包含"hello"的所有行 (d
)。
另一方面,:%s
只是在整个文件中执行搜索(使用正则表达式)和替换:
:%s/hello/world/g
g
在结尾处表示 global
或者 greedy
(这有争议),所以它将替换行中的所有出现,而不仅仅是每行一个。如果你想要手动确认每个替换,可以使用 c
标志(:%s/hello/world/gc
)。hello
替换为 world
。
:g
和 :%s
命令都支持正则表达式。
s
命令表示 substitute
,%
表示整个缓冲区。因此,%s
表示在整个缓冲区内进行替换。你也可以指定行范围::10,15s/hello/world/g
这将仅在第10至15行(包括第10和第15行)上执行之前所看到的搜索和替换操作。
:g
命令只在当前缓冲区执行,与 :s
命令相同。它们之间唯一的区别在于默认范围::s
命令等同于 :.s
,而 :g
命令等同于 :%g
。因此,:%s/pat/subs
和 :%g/pat/ cmd
(或 :g/...
)将作用于缓冲区中所有匹配模式的行,而 :.s/pat/subs
(或 :s/...
)和 :.g/pat/ cmd
将作用于当前行,如果它匹配模式。 - mMontug
在末尾(//g)更有可能是代表正则表达式中常用的 贪婪
(greedy)的缩写,包括在原始的 PCRE API 中也是如此。它基本上表示“尽可能多地匹配多个结果”。尝试编辑但没有成功,所以只能用注释了。 - miyalys:g/pattern/d
删除字符串将破坏您的编号寄存器,将它们向下推。而像 :%s/pattern//g
这样替换为空则不会这样做。 - paradroid:d
后面加上下划线可以避免覆盖寄存器。而且速度更快。请参阅 :h :g
。 - undefined它们是不同的。
:g
可以为匹配的行执行命令,其中之一就是 :s
。也就是说,你可以结合使用 :g 和 s
。
:%s
在整个缓冲区中进行搜索和替换,虽然它也可以使用表达式做一些其他事情,但不如 :g
直截了当。
例如:
:g/foo/s/bar/blah/g
这将对包含foo
的行进行bar->blah
替换。使用:s
,我们可以:
这将对包含foo
的行进行bar->blah
替换。使用:s
,我们可以:
:%s/foo/\=substitute(getline('.'), 'bar','blah','g')
所以,:g
更容易。
因此,如果你正在处理替换任务,通常应该首先考虑使用:s
。如果你想做类似于对所有匹配 xxx 的行,我想删除/合并/缩进/......的操作,:g
可能会对你有帮助。
vi或vim中的“:”模式(如ex-mode)命令采用以下形式:
[Address-specifier] [command] [command-specifics] [cmd-modifiers]
地址可以是单行地址(ex-mode 操作“行”),也可以是一个行范围。
例如,在“p”命令中非常简单的一个命令,它将打印被寻址的行。
:1p - will print line 1.
:5p - will print line 5.
:1,5p - will print lines 1 through 5. 1,5 is an address range.
:7,+3p - will print lines 7 through 10 (7,7+3=10). A relative range.
在地址空间中有一些简写方式。$和%是最常用的。
$ means "last line in the file". Thus the expression:
1,$p - will print all lines, 1 to the LAST-line in the file.
表达式1,$在IT技术中被广泛使用(例如将以下命令应用于文件中的所有行),因此它甚至拥有一个更短的简写符号%。%的意思是"1,$"。
%p - will print all lines, 1 to the LAST-line in the file, just like 1,$
还有一个特殊的“全局”命令,其作用是提供一组地址前缀,这组地址前缀不一定是线性行范围,而是由正则表达式匹配确定的。":g / regex /"前缀适合ex命令格式的“地址说明符”部分(而不是其后面的命令部分)。它允许指定一组由正则表达式而不是“行号”或“行范围”匹配的行。匹配应用于正则表达式出现在行中,然后该行包含在命令将应用于其中的行列表中。
以以下文件为例:
1: 1
2: 1 2 3 4 5 6 1 2
3: 3 2 1
4: 2 3 1 2
使用全局前缀/地址正则表达式和 "p" 打印命令的此命令:
:g/1 2/p - will print
2: 1 2 3 4 5 6 1 2
4: 2 3 1 2
第2行和第4行都与正则表达式:g/1 2/匹配,并有效扩展为行号列表,对列表中的每个项目应用以下命令。(类似于此命令):
:2p 4p
替换命令允许用其他文本替换与正则表达式匹配的字段。如果我们在示例文件的第2行应用替换命令,我们可以看到其效果。
1: ....
2: 1 2 3 4 5 6 1 2
3: ....
命令:
:2s/1 2/2 1/ will change line 2 to be instead: 2: 2 1 3 4 5 6 1 2
它仅更改模式“1 2”的第一个实例为“2 1”。 如果我们使用“u”撤消此命令,则可以修改后再次运行该命令。 我们可以在命令上使用“p”修饰符,在“替换”中不会有太多作用。 它应用更改,但也在屏幕底部打印已应用的更改(在本例中有些多余)。
:2s/1 2/2 1/p
u (to undo), and then we try it again.
我们可以使用 "c" 修改器来请求确认。:2s/1 2/2 1/c The "confirm" modifier for the substitute asks for confirmation on each change.
取消上一步操作(撤销):u。
“全局”修饰符(不是全局地址/正则表达式地址操作符)可以使替换命令对一行执行多个替换。
:2s/1 2/2 1/g - The "g" here is a modifier to the "s" substitute command.
这意味着在整行上全局执行替换操作。修饰符修改“命令”,而命令适用于一个或多个行,即设置地址字段。在替换命令末尾应用“g”表示:在此行上全局替换,例如每次替换命令的正则表达式出现时,都执行替换。
2: 2 1 3 4 5 6 2 1 - Here, both the first and second instance are substituted.
如果替换命令无法找到匹配的正则表达式,则不执行任何操作。这意味着它可以应用于一系列行,并且只对至少有一个与替换命令正则表达式匹配的行产生影响。
1,4 s/1 2/2 1/
1,$ s/1 2/2 1/
%s/1 2/2 1/
都是等价的,它们将使用替代命令正则匹配模式来代替第一个出现的匹配项,并用替代模式进行替换。
1: 1
2: 1 2 3 4 5 6 1 2
3: 3 2 1
4: 2 3 1 2
变成:
1: 1
2: 2 1 3 4 5 6 1 2
3: 3 2 1
4: 2 3 2 1
在末尾添加 "g" 后变为:
:%s/1 2/2 1/g
1: 1
2: 2 1 3 4 5 6 2 1
3: 3 2 1
4: 2 3 2 1
:g/regex/ 地址说明符适用于其后的任何命令,并且该命令可以包括替换命令,包括 "g" 修饰符。
:g/3 4/s/1 2/2 1/g
这个命令的作用是说:"全局匹配符合正则表达式 /3 4/ 的行",然后执行命令。
:s/1 2/2 1/g.
只有第二行包含正则表达式 /3 4/,所以只有第二行匹配。 因此在此文件上:
:g/3 4/s/1 2/2 1/g is equivalent to:
:2s/1 2/3 4/g, which substitutes all occurrences of 1 2 with 2 1.
1: 1
2: 1 2 3 4 5 6 1 2
3: 3 2 1
4: 2 3 1 2
变成:
1: 1
2: 2 1 3 4 5 6 2 1
3: 3 2 1
4: 2 3 1 2
注意:第 4 行未更改,因为它在地址指示符匹配行中没有模式"3 4"。
:g/regex-line-match/s/match-regex-substitute/sub-pattern/g
:%s/match-regex-substitute/sub-pattern/g
这两行通常在作用上可以等效。但它们也经常无法等效。等效性取决于正则表达式模式和它们的匹配,因为当一行没有匹配的匹配-正则表达式-替换匹配模式时,“替换”不会执行任何操作。
% = 1,$ which matches all lines, and then applies the substitute pattern.
:g/./ would match every line, if prefixed.
如果全局/正则表达式前缀的正则表达式模式与替换的匹配模式相同,则会有很多额外的输入,但它将限制替换命令仅适用于与全局/正则表达式匹配的行。 如果全局/正则表达式表达式确实匹配每一行,例如:g/^.$/,那么全局行将产生与%相同的效果。(因为%将匹配所有行,而:g/^。 $ /将匹配全部行,所以在基本情况下“s”将执行相同的操作。当使用更典型的正则表达式(匹配某些特定字符串)时,:g / regex /前缀将不同于%。该命令“s”仅适用于首先与g:/ refex /前缀匹配的行,而不是应用于所有行1,$。然后,替换将尝试成功地应用其自己的“每行”匹配模式(和替换),或者在给定的行上找不到匹配并且什么也不做。
全局/正则表达式前缀有趣的地方在于,当全局/正则表达式前缀正则表达式与替换匹配正则表达式模式不同时,您需要首先应用全局/正则表达式(以确定将针对哪些行),然后应用替换命令中的“匹配-替换-正则表达式”模式(可以不同)。 正如我们上面的示例所示,我们使用了“3 4”的全局/正则表达式前缀,以及应用于其次的“1 2”的替换匹配正则表达式模式。
非常高级:
虽然全局/正则表达式本质上是构建要应用命令的行列表,但构建该列表的方式与1,$或其他固定范围指定符不同。 固定指定符在键入:[address]command时“一次性”计算。 另一方面,全局/正则表达式命令在每个子命令的单个应用后重新计算其行目标。
我们将使用“join”命令来说明差异。
1: 1
2: 1 2 3 4 5 6 1 2
3: 3 2 1
4: 2 3 1 2
如果我使用范围语法指定一系列要应用“join”命令的命令,例如::1,$j(或者:%j),则会产生以下结果:
1: 1 1 2 3 4 5 6 1 2 3 2 1 2 3 1 2
这种情况是由于1,$在开始时选择了第1行和第4行,然后对每个选定的行应用“j”,将范围内的所有行合并。
但是,如果我们改为使用全局前缀操作符(匹配所有行),应用程序就不同:
:g/./j
这将呈现:
1: 1 1 2 3 4 5 6 1 2
2: 3 2 1 2 3 1 2
因为在两种语法中,“命令”何时以及如何应用的方式不同,所以会出现差异。在第一种“:%j”语法中,所有行都会先计算出来,然后对每一行应用“j”命令。
而使用全局/正则表达式语法时,则是基于“随时随地”和“从当前位置”应用行和命令。因此,“:g/./j”命令首先匹配LINE1,然后运行“j”将行1和行2组合成新的第一行。接着它会前进到文件的“下一行”(即新文件new-2),匹配该行(/./匹配所有内容)并将“j”应用于new-2(原始的第三行)和new-3(原始的第四行)以创建新的new-new-2 = 3+4。然后再前进到“新的新文件”的下一行,这是第3行(但没有新的new-new-3,所以它停止了)。结果为:
1: 1 1 2 3 4 5 6 1 2
2: 3 2 1 2 3 1 2
关键区别在于应用命令实例后,全局正则表达式搜索会在应用命令后的文件中的“下一行”上继续。
正如早期的帖子总结得那样(但假设读者拥有更多知识):
:g/first-search-pattern/s/match-pattern/substitute-pattern/g or /gc for confirm.
这些模式可以是不同的,末尾的g或gc可以存在(每行上的所有出现次数,带有或不带有确认),也可以省略(仅限每行的第一次出现)。在编写时:
:%s/pattern/replace/g is common, the following is nearly equivalent:
:g/./s/pattern/replace/g (less common, but basically the with "substitute" command).
:g[lobal]
命令和选项和变量的g:[name]
前缀混淆了。 - Ingo Karkat:help tutor
命令即可。 - mMontu:help
开始,有很多信息可供查阅。在|usr_10.txt| Making big changes
章节中,进一步解释了:s
和:g
命令。 - mMontu