:[range]s[ubstitute]/{pattern}/{string}/[flags]
| 参数 | 说明 |
|---|---|
| ranges | 范围 |
| s[ubstitute] | 替换命令 |
| 匹配表达式,可以是正则表达式。,用于匹配对应文字。 | |
| 替换后表达式,可以是正则表达式,用于替换后 |
| 标志位 | 说明 |
|---|---|
| & | 是否需要重复上次查找命令 |
| c | 在替换时确认是否替换 |
| e | 如果模式没有匹配,则不会报错 |
| g | 无需确认全部替换 |
| i | 不区分大小写 |
| I | 强制区分大小写 |
| n | 只统计匹配的数量,不进行替换 |
| p | 打印出替换成功的最后一行 |
| # | 打印出替换成功的最后一行,包括行数 |
| l | 跟p 一样只不过 ,要打印出$ 符号 |
| r | 查找模式是否重用 |
将substitute命令的查找域留空,意味着Vim将会重用上次的查找模式。可以利用这一特点精简工作过程。
另外需要注意一点,把查找域留空,会在命令历史中留下一项不完整的记录。由于模式通常保存在Vim的查找历史记录中,substitute命令则保存于Ex命令的历史记录中(参见 :h cmdline-history )。因此,将查找任务与替换任务分离,会致使这两组信息被单独存放,从而导致当再想重用之前的substitute命令时,会遇到困难。
:%s/<C-r>//“\1”/g
在使用substitute命令时将查找域留空,有时很方便,有时却很麻烦。两种方法都体验一下,你就会形成自己的直觉,并依此来判断使用的时机。就像我常说的那样,要靠你自己的判断。
magic nomagic action
& \& replaced with the whole matched pattern s/\&
\& & replaced with &
\0 replaced with the whole matched pattern \0 s/\0
\1 replaced with the matched pattern in the first
pair of () s/\1
\2 replaced with the matched pattern in the second
pair of () s/\2
.. .. s/\3
\9 replaced with the matched pattern in the ninth
pair of () s/\9
~ \~ replaced with the {string} of the previous
substitute s~
\~ ~ replaced with ~ s/\~
\u next character made uppercase s/\u
\U following characters made uppercase, until \E s/\U
\l next character made lowercase s/\l
\L following characters made lowercase, until \E s/\L
\e end of \u, \U, \l and \L (NOTE: not <Esc>!) s/\e
\E end of \u, \U, \l and \L s/\E
<CR> split line in two at this point
(Type the <CR> as CTRL-V <Enter>) s<CR>
\r idem s/\r
\<CR> insert a carriage-return (CTRL-M)
(Type the <CR> as CTRL-V <Enter>) s/\<CR>
\n insert a <NL> (<NUL> in the file)
(does NOT break the line) s/\n
\b insert a <BS> s/\b
\t insert a <Tab> s/\t
\\ insert a single backslash s/\\
\x where x is any character not mentioned above:
Reserved for future expansion
实际上,不必手动输入完整的替换字符串。如果某段文本已在当前文档中出现,可以先把它复制到寄存器,再通过传值或引用的方式将寄存器的内容应用至替换域。
我们已在技巧91中看到,当substitute命令的查找域为空时,Vim做出了智能的选择。人们不禁会想,如果将替换域留空的话,substitute命令也一样会重用上一次的字符串吧?但事实并非如此。将替换域留空,意味着substitute命令会用空的字符串替换每一处匹配。换句话说,所有的匹配都被删除了。
输入 <C-r>{register},可以将寄存器的内容插入命令行。假设我们已经复制了一些文本,如果要将它们粘贴到substitute命令的替换域,需要输入以下命令。
➾ :%s//<C-r>0/g
当输入 <C-r>0时,Vim会把寄存器0的内容粘贴进来,这意味着我们可以在执行substitute命令之前对其进行一番检查。在大多数情况下,它工作得都很好,但也引入了新的问题。
如果寄存器0中的文本包含了在替换域中具有特殊含义的字符(例如 & 或 ~),就必须手动编辑这段文本,对这些字符进行转义。另外,如果寄存器0包含多行文本,有可能在命令行上显示不全。
为了避免这些问题,可以在替换域中简单地引用某个寄存器,从而得到该寄存器的内容。
假设已经复制了多行文本,并存放于寄存器0中。我们现在的目标是在substitute命令的替换域中使用这段文本。通过运行以下命令,可以做到这一点。
➾ :%s//\=@0/g
替换域中出现的 \= 将指示Vim执行一段表达式脚本。在Vim脚本中,可以用 @{register} 来引用某个寄存器的内容。具体来说,@0会返回复制专用寄存器的内容, @" 则返回无名寄存器的内容。因此,表达式 :%s//\=@0/g表示Vim将会用复制专用寄存器的内容替换上一次的模式。
先看一下这条命令。
➾ :%s/Pragmatic Vim/Practical Vim/g
再与以下命令序列进行比较。
➾ :let @/='Pragmatic Vim'
➾ :let @a='Practical Vim'
➾ :%s//\=@a/g
其中,:let @/='Pragmatic Vim' 是采用编程的方式输入查找模式,它等同于直接执行查找命令 /Pragmatic Vim<CR>(有一点不同,即运行 :let @/='Pragmatic Vim' 不会在查找历史中留下任何记录)。
同样的道理,:let @a='Practical Vim' 表示设置 a 寄存器的内容。它等同于高亮选中“Practical Vim”并用 "ay将选中的文本存入寄存器 a。
这两条substitute命令都完成同一件事,即把所有“Pragmatic Vim”替换为“Practical Vim”。但要考虑它们各自的影响。
第一种方法会在命令历史中留下一项内容为 :%s/Pragmatic Vim/Practical Vim/g的记录,使人一目了然。在稍后的编辑过程中,如果我们意识到要重复这条命令,可直接从命令历史中调出该项记录,即可加以执行。总之,不会有什么意外发生。
而第二种方法会在命令历史中留下一项内容为 :%s//\=@a/g的记录。这看上去是不是相当神秘呢?
试想一下,首次运行substitute命令时,查找模式为“Pragmatic Vim”,而寄存器a包含文本“Practical Vim”。但是半小时之后,当前的查找模式可能已经被多次修改了,而且寄存器 a也可能被其他内容覆盖。因此,如果重复 :%s//\=@a/g命令,结果会与第一次执行这条命令时截然不同。
可以利用这一特点。首先,查找要操作的文本,并将替换的内容复制到寄存器a中。之后,可以重复调用命令 :%s//\=@a/g,而该命令会使用刚刚被赋值的 @/ 和 @a中的内容。接下来,可以查找新的文本,并复制新的替换字符串至寄存器 a。而当再次重复 :%s//\=@a/g命令时,运行结果将会迥然不同。
此法不妨一试。你或许会爱上它,也可能会讨厌它。但无论哪种情况,都是不错的技巧。