*repeat.txt* For Vim version 7.1. 最近更新: 2007年3月 VIM 参考手册 by Bram Moolenaar 译者: Willis http://vimcdoc.sf.net 重复命令,Vim 脚本和调试 *repeating* 用户手册第 26 章 |usr_26.txt| 介绍了重复命令。 1. 单次重复 |single-repeat| 2. 多次重复 |multi-repeat| 3. 重复组合操作 |complex-repeat| 4. 使用 Vim 脚本 |using-scripts| 5. 调试脚本 |debug-scripts| 6. 刨视 |profiling|
1. 单次重复 *single-repeat* *.* . 重复上次改变,有计数前缀的重复[count]
次。 如果 'cpoptions' 包括 'y' 标志, 也可以用来重复一个抽 出 (yank) 操作。不能重复命令行命令。 简单的改变操作可以用 "." 重复。如果没有数字前缀,使用上次的改变的重复次数。如 果输入一个重复次数,就用它取代上次的计数。如果上次改变的操作使用数字编号的寄存 器,则逐次递增使用寄存器的序号。一个使用的示例可以参见 |redo-register|。注意, 如果重复的命令涉及可视的选择区域,使用相同_大小_的区域。参见 |visual-repeat|。 *@:* @: 重复上次命令行[count]
次。 {仅当编译时加入 |+cmdline_hist| 特性才可用}
2. 多次重复 *multi-repeat* *:g* *:global* *E147* *E148* :[range]
g[lobal]/{pattern}
/[cmd]
在[range]
界定的匹配模式{pattern}
的文本行上执行 Ex 命令 |cmd
| (缺省是 ":p")。 :[range]
g[lobal]!/{pattern}
/[cmd]
在[range]
界定的_不_匹配模式{pattern}
的文本行上执 行 Ex 命令 |cmd
| (缺省是 ":p")。 *:v* *:vglobal* :[range]
v[global]/{pattern}
/[cmd]
等同于 :g!。 除了用 '/' 来包围{pattern}
以外,你可以用任何其它单字节字符,但不包括字母数 字、'\'、'"' 或 '|'。这对你想在搜索模式或者替代字符串里包含 '/' 很有用。 模式的定义见 |pattern|。 global 命令先扫描[range]
界定的行,然后对每个匹配的行进行标记 (对于一个跨行的 匹配,则只针对开始的一行)。 第二次扫描对每个标记的行,并在其之前附加上行号,执行指定的 |cmd
| 命令。 ":v" 和 ":g!" 命令针对未标记的行进行。如果一行被删除,其标记也自动消失。 缺省的[range]
是整个缓冲区 (1,$)。用 "CTRL-C
" 中断该命令。如果某行的执行给 出错误,该行的执行被中断,但 global 命令仍从下一个被标记或未被标记的行继续。 要重复一个非 Ex 的命令, 你可以使用 ":normal" 命令: :g/pat/normal{commands}
要确保{commands}
以完整的命令结束,不然 Vim 会等待你输入该命令余下的部分,对 每次匹配都将如此。由于屏幕此时还未更新,你恐怕会因此不知所措。参见 |:normal|。 撤销/重做命令将会针对整个 global 命令。换而言之,前次上下文标记只有在命令开头 会设一次 (用 "''" 可以回到 global 之前的光标位置)。 global 命令也设置最近使用的搜索模式和最近使用的替换模式 (后者是 vi 兼容的),以 便于全局替换一个字符串::g/pat/s//PAT/g
该命令替换所有的 "pat" 为 "PAT"。同样的功能也可以用::%s/pat/PAT/g
实现。这样省下了两个字符! 使用 ":visual" 命令是例外。此时,移动到匹配行,进入普通模式并在那里执行命令, 直到你用 |Q| 回到 Ex 模式为止。对每个匹配行重复此操作。这么做的时候,不能用 ":global"。
3. 重复组合操作 *complex-repeat* *q* *recording* q{0-9a-zA-Z"}
在寄存器{0-9a-zA-Z"}
里记录键入的字符 (大写名字的寄存 器表示附加键入的内容)。'q' 命令不能在执行寄存器时使 用。同样,在映射里也不能。{Vi: 没有记录功能}
q 停止记录。(注意 实现细节: 停止记录的 'q' 不会被保存在 寄存器里,除非它是映射的结果){Vi: 没有记录功能}
*@* @{0-9a-z".=*} 执行寄存器 {0-9a-z".=*} 的内容[count]
次。 注意 不能 用寄存器 '%' (当前文件名) 和 '#' (轮换文件名) 。"@=" 则会提示你输入一个表达式。这个表达式的结果会被执行。参 见 |@:|。{Vi: 只有命名寄存器可用}
*@@* *E748* @@ 执行上次执行的 @{0-9a-z":*}
[count]
次。 :[addr]*{0-9a-z".=} *:@* *:star* :[addr]@{0-9a-z".=*} 把寄存器 {0-9a-z".=*} 里的内容看成一个 Ex 命令执行。首 先,把光标放在 [addr] 行 (缺省是当前行) 上。如果 'cpoptions' 设定了 'e' 标志位而寄存器的内容最后一行没 有<CR>
,会自动加上。 注意 ":*" 命令只有在 'cpoptions' 设置 '*' 标志位时才会 识别。如果用 'nocompatible',这_并_非缺省。 ":@=" 使用最近使用的表达式。表达式执行的结果被当作 Ex 命令执行。 这些命令里,不识别映射。{Vi: 只有某些版本有此功能}
将来: 会对地址范围里每一行执行寄存器的命令。 *:@:* :[addr]@: 先把光标放在 [addr] 行 (缺省是当前行) 上,然后重复上次 的命令行。{Vi 无此功能}
*:@@* :[addr]@@ 先把光标放在 [addr] 行 (缺省是当前行) 上,然后重复上次 的 :@{0-9a-z"}
。{Vi 只有某些版本有此功能}
4. 使用 Vim 脚本 *using-scripts* 参阅用户手册第 41 章 |usr_41.txt| 了解如何写 Vim 脚本。 *:so* *:source* *load-vim-script* :so[urce]{file}
从{file}
里读取 Ex 命令,即 ":" 开头的命令,并执行。 激活 |SourcePre| 自动命令。 :so[urce]!{file}
从{file}
里读取 Vim 命令,就像你在普通模式下键入的命 令一样。 如果要执行的命令在 |:glboal
|、|:argdo|、|:windo| 或 |:bufdo| 之后、在循环体内、或者有另外一个命令紧跟其 后,那么执行时不会更新屏幕显示。{Vi 无此功能}
*:ru* *:runtime* :ru[ntime][!]{file}
.. 从 'runtimepath' 指定的目录里查找{file}
。从匹配的文件 里读取 Ex 命令。如果没有匹配的文件,不报错。例如::runtime syntax/c.vim
可以指定多个以空格分隔的
{file}
参数。每个{file}
都从 'runtimepath' 指定的第一个目录开始查找,然后是第二个、 第三个,等等。{file}
里可以通过加反斜杠来包含空格 (不 过,为了避免麻烦,最好不要在文件名里使用空格)。 如果使用了 [!],所有找到的文件都被执行。否则,只执行第 一个找到的文件。 如果{file}
包含通配符,它被扩展为所有的匹配文件名。例 如::runtime! plugin/*.vim
< 这是 Vim 启动的时候启动插件所使用的命令。类似的::runtime plugin/*.vim
只会执行其中的第一个文件。 当 'verbose' 至少为一时,如果没有文件找到,会显示信息。 当 'verbose' 至少为二时,对每个搜索到的文件都会提示信 息。{Vi 无此功能}
:scripte[ncoding] [encoding] *:scripte* *:scriptencoding* *E167* 指定脚本使用的字符编码。后续以 [encoding] 编码的脚本行 会被转换成 'encoding' 选项所指定的编码,如果两者不同的 话。如:scriptencoding iso-8859-5
scriptencoding cp932
如果 [encoding] 为空,则不作任何转换。这可以用来避免对 一些行进行转换:scriptencoding euc-jp
... 被转换的行 ...
scriptencoding
... 不被转换的行 ...
如果系统不支持所需的转换,不会有错误信息,但转换也不会 发生。 不要用 "ucs-2" 或者 "ucs-4"。Vim 不能用这些编码的脚本 (因为其中会有 NUL 字节)。如果一个待执行的脚本以一个 BOM (Byte Order Mark 字节顺序标记) 开头,Vim 会识别其 为 utf-8 编码,从而无须指定 ":scriptencoding utf-8"。 如果编译时没有指定 |+multi_byte| 特性,这个命令会被忽 略。
{Vi 无此命令}
*:scrip* *:scriptnames* :scrip[tnames] 列出所有执行过的脚本名字,以它们初次执行之顺序排列。排 列的次序号码被用作相应的脚本 ID |<SID>|。{Vi 无此功能}
{仅当编译时加入 |+eval| 特性才有功能} *:fini* *:finish* *E168* :fini[sh] 停止执行脚本。只能用在 Vim 脚本中,来快速跳过脚本的其 余内容。如果出现在 |:try| 之后但在相应的 |:finally| (如果存在的话) 之前,":finally" 到 |:endtry| 的内容还 会执行。执行完所有嵌套的 ":try" 层的 ":finally" 代码 后,最外层的 ":endtry" 才会最终真正停止脚本的执行。{Vi 无此功能}
所有的命令和命令序列可以通过把它们放在命名的寄存器里执行来重复调用。有两个方法 可以把命令放在寄存器里: - 用记录命令 "q"。可以键入一串命令,在执行的同时它们被存入一个寄存器里。这很简 明,因为你能看见你所做的事情。如果你敲错了,用 "p" 把寄存器的内容 "放置" 在 一个文件里,然后编辑这个命令序列,把它们再次放回到 (比如,用删除命令) 寄存器 里。你也可以用大写名字的寄存器名来附加命令,从而继续上次的纪录。 - 删除 (delete) 或者抽出 (yank) 命令序列到寄存器。 常用的命令序列可以用 ":map" 命令映射到一个功能键上。 另外一个办法则是把命令写到一个文件里,用 ":source!" 命令执行之。这对很长的命令 序列有用。你甚至可以把它和 ':map' 命令混合使用,从而用一个功能键来储存复杂的功 能。 ':source' 命令从文件里逐行读取 Ex 命令。如果其间需要键盘输入,你需要自己键入。 'source!' 命令从脚本里逐字读取命令,就像你自己敲入每一个字符一样。 示例: 如果你给出一个 ':!ls' 命令,你得到一个 |hit-enter| 提示。如果你用 ':source' 执行包含 '!ls' 一行的文件,你必须按一个回车。不过如果你用 ':source!' 来执行包含 ':!ls' 的文件,其后的字符会依次读入,直到遇到一个<CR>
为止。你不需 要通过键盘键入这个<CR>
,除非 ":!ls" 是文件的最后一行。 在脚本里当然也可以有 ':source[!]' 命令,从而可以建立一个自顶而下的脚本调用树。 ':source' 命令允许的嵌套深度由同时打开的最大的文件数目决定 (大概 15 个左右), ':source!' 命令许可的嵌套深度则最多为 15 层。 在被执行的文件里,你可以在需要文件名的地方用 "<sfile>
" 字符串 (直接的文本,不 是一个特殊的键)。它会被被执行的文件的名字来代替。例如,如果你在 ".vimrc" 文件 相同的目录里有一个 "other.vimrc" 文件,你可以在 ".vimrc" 里如此调用它::source
<sfile>
:h/other.vimrc在脚本文件里,依赖于终端的键码由不依赖于终端的两个字符码代表。这样,他们就能在 不同的终端里代表相同的意义。这里,第一个字符码是 0x80 或者 128,屏幕上显示 为 "~@"。第二个字符可以在 |key-notation| 列表里找到。这些编码也可以用
CTRL-V
加上一个三位数字的十进制码来键入。这个方法_不_适用于<t_xx>
termcap 代码,它们 只能用在映射里。 *:source_crnl* *W15* MS-DOS、Win32 和 OS/2: 用 ":source" 执行的文件通常每行以<CR>
<NL>
结尾。这没有 问题。在 'fileformats' 非空并且第一行不以<CR>
结尾的时候,用<NL>
的行结尾 (比如,Unix 编写的文件) 会被识别。不过,如果第一行里有 ":map<F1>
:help^M" 这 样的内容 (其中 ^M 是一个<CR>
),这个机制会失败。如果第一行以<CR>
结尾,但其后的行不是,那你会得到错误信息,因为第一行里的<CR>
会丢失。 Mac Classic: 用 ":source" 执行的文件通常每行以<CR>
结尾。这没有问题。在 'fileformats' 非空并且第一行不以<CR>
结尾的时候,用<NL>
的行结尾 (比如,Unix 编写的文件) 会被识别。如果用<NL>
行结尾的时候,要小心第一行不要有<CR>
。 在别的系统上,Vim 期待 ":source" 执行的文件以<NL>
行结尾。这没有问题。如果你 的文件用<CR>
<NL>
结束行 (比如,MS-DOS 编写的文件),所有的行都会有一个附尾的<CR>
。有些命令 (比如映射命令) 会因此有问题。这里不使用自动换行符识别机制,因为 第一行出现以<CR>
结尾的映射命令很常见,自动机制这时容易出错。 *line-continuation* ":source" 执行的 Ex 命令脚本里的长行可以用通过在下一行的开始插入续行符 "\" (反 斜杠) 来分开。反斜杠之前可以出现空格,它们将被忽略。 示例: 如下几行:set comments=sr:/*,mb:*,el:*/,
\://,
\b:#,
\:%,
\n:>,
\fb:-
会被解读为一行::set comments=sr:/*,mb:*,el:*/,://,b:#,:%,n:>,fb:-
每行反斜杠之前的所有引导空白字符会被忽略。注意: 在此之前的一行的行尾的空格可能 不能随便添加;这由命令分开的位置决定,那里也许可以也许不可以有额外的空格。 在用 ":append" 和 ":insert" 命令时,有一个问题:
:1append
\asdf
.
反斜杠被看作续行符,所以这等价于以下命令::1appendasdf
.
要避免这一点,在 'cpoptions' 选项里加上 'C' 标志位::set cpo+=C
:1append
\asdf
.
:set cpo-=C
要 注意 在函数里的命令里用到这些命令时,你需要在定义函数的时候加上 'C' 标志 位,不是在执行函数的时候。
:set cpo+=C
:function Foo()
:1append
\asdf
.
:endfunction
:set cpo-=C
原理: 许多程序用行尾的反斜杠来指示行要继续。如果这样,Vim 会和 Vi 不兼容。例 如下面的 Vi 映射:
:map xx asdf\
因此,只能用特殊形式的出现在行首的反斜杠。
5. 调试脚本 *debug-scripts* 除了在自己的脚本里提示明显的消息,Vim 提供了调试模式来让你了解自己的代码在做什 么。你可以单步执行脚本文件和函数和设置断点。 请 注意: 调试模式远未完善。调试程序会对 Vim 的工作产生副作用。你不能用它调试一 切细节。例如,调试信息会弄乱屏幕的显示。{Vi 没有调试模式}
另外一个办法是设置 'verbose' 选项。设置一个比较大的数字,你会得到 Vim 在做什么 的更详尽的信息。 启 动 调 试 模 式 *debug-mode* 以下方法可以进入调试模式: 1. 用 |-D| 参数启动 Vim:vim -D file.txt
调试会在执行第一个 vimrc 文件的时候开始。这有助于了解 Vim 启动的时候干了些 什么。一个副作用是 Vim 会在初始化完成之前切换终端模式,这会有意想不到的后 果。 对只用 GUI 的版本 (Windows、Macintosh) 调试会在 GUI 窗口打开的一刻开始。要 提早进入调试,在 vimrc 文件里加上 ":gui" 命令。 *:debug* 2. 执行命令前加上 ":debug" 前缀。这样,调试只对这个命令进行。这对调试某一个特 定的脚本和用户函数,或者 autocommands 用到的脚本和函数有用。例如::debug edit test.txt.gz
3. 在一个执行的文件和用户函数里设置断点。你可以在命令行里这么做:
vim -c "breakadd file */explorer.vim" .
这会启动 Vim 并在 "explorer.vim" 脚本的第一行停下。进入调试模式后也可以设置 断点。 在调试模式里,每个命令都会在执行前被显示。注释行、空行和其他不执行的行会被跳 过。如果一行里有两个 "|" 分隔的命令,它们被分别显示。 调 试 模 式 进入调试模式以后,可以使用通常的 Ex 命令。比如,要检查某变量的值:echo idx
在一个用户函数里,这会显示局域变量 "idx" 的值。在变量前加上 "g:" 可以得到全局 变量的值:echo g:idx
所有的命令都在当前函数或脚本的上下文下执行。你可以设置选项,比如设置或者重设 'verbose' 来显示当前发生的事情,但是你需要在执行你感兴趣的行之前设置它们::set verbose=20
要避免更新屏幕的命令,因为直到退出调试模式之前,无法看到它们的效果。例如,帮助 命令:
:help
不会很有帮助。 调试模式有自己的命令行历史。 函数行的行号是相对于函数开始的地方的。如果你要知道你在哪里,在另外一个 Vim 里 编辑包含这个函数的文件,先找到函数的开始处,然后用 "99j",其中的 "99" 用实际的 行号代替。 另外,可以使用如下的命令: *>cont* cont 继续执行到下一个断点。 *>quit* quit 终止执行。这和CTRL-C
类似,但是还是有要执行的东西,并 不是所有的都中止。在遇到断点时还是会停下来。 *>next* next 执行一个命令,并在它结束时返回调试模式。步过用户函数 调用和被执行的脚本。 *>step* step 执行一个命令,并在它结束时返回调试模式。步入用户函数 调用和被执行的脚本。 *>interrupt* interrupt 类似于CTRL-C
,但不同于 ">quit",这会在执行下个命令前 返回到调试模式。它有助于测试 |:finally| 和 |:catch| 对中断例外的处理。 *>finish* finish 结束当前的脚本或者用户函数,并在调用该脚本或函数的行的 下一行之前返回到调试模式。 关于调试模式下的附加命令: - 没有它们的命令行自动补全。补全只对一般的 Ex 命令有效。 - 可以用一个字符来简写,即: "c"、"n"、"s" 和 "f"。 - 按<CR>
会重复上一个调试命令。在执行另一个命令之后,这个功能会被关掉 (因为不 知道你要重复什么)。 - 如果你想调用相同的名字的 Ex 命令,在前面附加一个冒号: ":cont"、":next"、 ":finish" (或者它们的缩写)。 定 义 断 点 *:breaka* *:breakadd* :breaka[dd] func [lnum]{name}
在函数上设置断点。例如::breakadd func Explore
它不会检查函数名的合法性。这样,断点可以在函数定义之前设置。 :breaka[dd] file [lnum]{name}
在一个脚本文件上设置断点。例如::breakadd file 43 .vimrc
:breaka[dd] here 在当前文件的当前行上设置断点。类似于:
:breakadd file
注意 只能用于执行此文件时会执行的命令,不适用于该文件此处定义 的函数。 其中 [lnum] 是断点所在的行号。Vim 会在它或它之后的行上停止。如果省略,就用行号 1。 *:debug-name*<cursor-line>
<current-file>
{name}
是用来匹配文件名或者函数名的模式。这和 autocommands 所有的模式类似。它 必须是一个完整匹配 (如同它以 "^" 开头和 "$" 结尾一样)。"*" 匹配任何字符序列。 它不用 'ignorecase' 选项,但是模式里可以用 "\c" 来忽略大小写 |/\c|。不要给函数 名加上 ()! 对脚本文件的匹配给予它的完整文件名。例如:breakadd file explorer
不会匹配。因为没有给出路径。breakadd file *explorer.vim
匹配 ".../plugin/explorer.vim" 和 ".../plugin/iexplorer.vim"。breakadd file */explorer.vim
只匹配 ".../plugin/explorer.vim"。 对函数名的匹配基于 ":function" 输出结果里它的显示方式。对于局部函数,这意味着 函数名前会附加上类似 "<SNR>
99_" 的字符串。 注意函数先被载入然后再执行。载入时,检查 "file" 断点,而执行时检查 "func" 断 点。 删 除 断 点 *:breakd* *:breakdel* *E161* :breakd[el]{nr}
删除断点{nr}
。用 |:breaklist| 可以看到每个断点的编号{nr}
。 :breakd[el] * 删除所有断点。 :breakd[el] func [lnum]{name}
删除函数断点。 :breakd[el] file [lnum]{name}
删除脚本断点。 :breakd[el] here 删除当前文件的当前行上的断点。 如果 [lnum] 省略,删除该函数或者文件的第一个断点。{name}
必须和 ":breakadd" 所匹配的名字完全相同。"explorer"、"*explorer.vim" 和 "*explorer*" 是不一样的。 列 出 断 点 *:breakl* *:breaklist* :breakl[ist] 列出所有的断点。 不 常 用 的 *:debugg* *:debuggreedy* :debugg[reedy] 从正常的输入流而不是直接从用户输入里读取调试模式命令。这只对测 试脚本有用。例如:echo 'q^Mq' | vim -e -s -c debuggreedy -c 'breakadd file script.vim' -S script.vim
:0debugg[reedy] 撤销 ":debuggreedy": 从用户那里直接读取调试模式命令,而不为调 试命令预读取。
6. 刨视 *profile* *profiling* 进行刨视意味着 Vim 测量执行函数和/或脚本时使用的时间。为此,需要 |+profile| 特 性。只有 Vim 编译时使用了 "huge" (巨大) 特性包才会包含此特性。{Vi 没有刨视功能}
你也可以用 |reltime()| 函数来测量时间。这只需要 |+reltime| 特性,它更常出现。 :prof[ile] start{fname}
*:prof* *:profile* *E750* 启动刨视,退出时把结果写到{fname}
文件。如果{fname}
已存在, 安静地覆盖它。变量 |v:profiling| 设为一。 :prof[ile] pause 直到下个 ":profile continue" 为止,停止刨视。可用来执行一些不 需计算的操作 (例如,执行外部命令)。不能嵌套。 :prof[ile] continue 在 ":profile pause" 之后出现,恢复刨视。 :prof[ile] func{pattern}
刨视匹配模式{pattern}
的函数。 |:debug-name| 说明如何使用{pattern}
。 :prof[ile][!] file{pattern}
刨视匹配模式{pattern}
的脚本。 |:debug-name| 说明如何使用{pattern}
。 只刨视脚本自身,不包括其中定义的函数。 如果加入 [!],同时刨视脚本里定义的所有函数。但只有在此命令后载 入脚本才会有效。 :profd[el] ... *:profd* *:profdel* 停止对指定参数的刨视,|:breakdel| 说明其中的参数。 刨视总是用 ":profile start fname" 开始。Vim 退出时写入结果文件。这里是输出的示 例,加上行号是为了方便解释:1 FUNCTION Test2()
2 Called 1 time
3 Total time: 0.155251
4 Self time: 0.002006
5
6 count total (s) self (s)
7 9 0.000096 for i in range(8)
8 8 0.153655 0.000410 call Test3()
9 8 0.000070 endfor
10 " Ask a question
11 1 0.001341 echo input("give me an answer: ")
头部 (行号 1-4) 给出整个函数的时间。"Total" 时间是函数执行时使用的时间。 "Self" 时间是 "Total" 时间减去用于下面事项的时间: - 其它用户定义的函数 - 执行脚本 - 执行自动命令 - 外部 (外壳) 命令 行号 7-11 显示每个执行行花费的时间。不计算不执行的行。所以注释行从不参与计算。 Count 列显示每行执行的次数。注意 到第 7 行的 "for" 命令执行次数比后面的行要多 一。这是因为该行也被用来检测循环结束。 Vim 花费在等待用户输入的时间完全不参与计算。所以 input() 提示后你的响应时间不 相关。 刨视给出关于时间花费在哪里的很好的指示,但要记住很多因素会影响输出结果: - 测量时间的准确度取决于 gettimeofday() 系统函数。精确度可能是 1/100 秒,但显 示的时间仍以微秒计。 - 测量的是实际流逝的时间。如果其它进程繁忙,可能造成的延迟无法预测。你可能需要 多次运行刨视,并使用时间最少的结果。 - 如果一行内执行多个命令,你只会得到一个时间。要看到个别命令的时间,把行分开。 - 各行时间的加总几乎总会比整个函数的时间要少。中间有少许开销。 - Vim 退出前删除的函数不会产生刨视信息。如果需要,你可以检查 |v:profiling| 变 量::if !v:profiling
: delfunc MyFunc
:endif
- 在多处理器系统中,因为睡眠模式或者为省电而减低处理器频率等因素影响,刨视结果 可能不正常。 - "self" 时间对递归调用的函数是不正确的。 vim:tw=78:ts=8:ft=help:norl:
Generated by vim2html on 2008年 03月 27日 星期四 17:04:45 CST