vi从入门到入魔

炫耀是需要观众的,而炫耀恰恰让我们失去观众。

在?看看神级编辑器?(这是一个关于 vi 快捷键的梳理文章)


什么是 vi

vi 是一种 计算机文本编辑器,由美国计算机科学家 比尔·乔伊(Bill Joy) 完成编写,并于 1976 年以 BSD 协议授权发布。

vi 是 “Visual” 的不正规的缩写,来源于另外一个文本编辑器 ex 的命令 visual。

vi 是一种 模式编辑器。不同的按钮和键击可以更改不同的“模式”。在“插入模式”下,输入的文本会直接被插入到文档;当按下“退出键”,“插入模式”就会更改为“命令模式”,并且光标的移动和功能的编辑都由字母来响应,例如:“j” 用来移动光标到下一行;“k” 用来移动光标到上一行,“x” 可以删除当前光标处的字符,“i”可以返回到 “插入模式”(也可以使用方向键)。在“命令模式”下,敲入的键(字母)并不会插入到文档,这给新接触 vi 的用户带来混乱。

在“命令模式”下,多重文本编辑操作是由一组键(字母)来执行,而不是同时按下 <Alt>、<Ctrl> 和其他特殊键来完成。更多复杂的编辑操作可以使用多重功能基元的组合,比如说 “dw” 用来删除一个单词,“c2fa” 可以更改当前的光标处中 “a” 之前的文本。这就是说:对于熟练的 vi 用户可以更快的操作,因为双手就可以不必离开键盘。

早期的版本中,vi 并没有指示出当前的模式,用户必须按下“退出键”来确认编辑器返回“命令模式”(会有声音提示)。当前的 vi 版本可以在“状态条”中(或用图形显示)。最新的版本中,用户可以在“终端”中设置并使用除主键盘以外的其他键,例如:PgUp,PgDn,Home,End 和 Del 键。图形化界面的 vi(如 gvim)可以很好的支持鼠标和菜单。

直到 Emacs 的出现(1984年以后),vi 几乎是所有“黑客”所使用的标准UNIX编辑器。从 2006 年开始,作为 “单一 UNIX 规范”(Single UNIX Specification) 的一部分,vi 或 vi 的一种变形版本一定会在 UNIX 中找到。

直到现在,vi 仍然被广泛的使用,并且赢得 1991 年在 USENET 的票选;vi 比 Emacs 的 Bulkier 版本启动的更快,并且占内存更少。因此,甚至 Emacs 的追随者又重新使用 vi,并且作为邮件编辑器和其他小型编辑工作的首选。1999 年时,著名的欧莱礼出版社(O’reilly)卖出了比 Emacs 更多的 vi 参考书。

当救急软盘作为恢复硬盘崩溃的介质以来,vi 通常被用户选择,因为一张软盘正好存储下 vi,并且几乎所有人都可以很轻松的使用 vi。

在编辑器的大战中,vi 和 Emacs 是作战的双方。


vi 常用快捷键(注意大小写和多键)

学习 vi 命令最重要的是理解其思想!命令的用法远不止下面所述,你甚至可以自由结合!

光标控制

命令 光标移动结果
h 或 ^h 向左移动一个字符
j 或 ^j 或 ^n 向下移动一行
k 或 ^p 向上移动一行
l 或 Space 向右移动一个字符
G 移动到 文件 的最后一行
nG 或 n: 移动到 文件 的第 n 行
w 移动到下一个字的开头
W 移动到下一个字的开头,忽略标点符号
b 移动到前一个字的开头
B 移动到前一个字的开头,忽略标点符号
e 移动到下一个字的结尾
E 移动到下一个字的结尾,忽略标点符号
L 移动到 屏幕 的最后一行
nL 移动到 屏幕 的倒数第 n 行
M 移动到 屏幕 的中间一行
H 移动到 屏幕 的第一行
nH 移动到 屏幕 的正数第 n 行
( 移动到句子的开头,若已经在开头,则移动到上一个句子的开头
) 移动到句子的结尾,若已在结尾,则移动到下一个句子的结尾
{ 移动到段落的开头,若已经在开头,则移动到上一个段落的开头
} 移动到段落的结尾,若已在结尾,则移动到下一个段落的结尾
0 或 竖线 移动到当前行的第一列
n竖线 移动到当前行的第 n 列
^ 移动到当前行的第一个非空字符
$ 移动到当前行的最后一个非空字符|
|+ 或 Return 移动到下一行的第一个非空字符
- 移动到上一行的第一个非空字符

h、j、k、l、w、W、b、B、e、E、(、)、{、}、^、$、+、- 前面都可以带 n,代表第 n 个。表中只写了部分略有不同的 n 的用法,这足以展示 vi 的强大之处!

上面的“竖线”和“n竖线“是和 md 的表格冲突了...就是这个字符 |

添加文本

命令 插入动作
a 在 光标之后 插入文本
A 在当前行插入文本(就是最后一个字符之后)
i 在光标之前插入文本
I 在当前行之前插入文本(就是第一个字符之前)
o 在当前行下方插入新行
O 在当前行上方插入新行
:r fileName 读取文件 fileName 中的内容,并插入在当前行 之后
:nr fileName 读取文件 fileName 中的内容,并插入在当前行的第 n 行 之后
escape(就是 ESC) 回到命令模式
^v char 插入式忽略 char 的指定意义,这是为了插入特殊字符所设计的

删除文本

命令 删除动作
x 删除光标处的一个字符
nx 从当前光标处开始,删除后面的 n 个字符
X 删除光标前的一个字符
nX 从当前光标处开始,删除前面的 n 个字符
dw 删除后一个字
ndw 从光标处开始,往后删除 n 个字
db 删除前一个字
ndb 从光标处开始,往前删除 n 个字
dG 从光标所在的行开始,一直删除到文件结束
dd 删除整行
ndd 从光标所在的行开始,向后删除 n 行
:n,md 把文件的 n 到 m 行删了
d 或 d$ 从光标处一直删到行尾
^h 或 Backspace 插入时,删除光标前面的字符
^w 插入时,删除光标前面的字

替换文本

命令 替换动作
rchar 用 char 替换当前字符(注意和 r 没有空格)
R text escape 用 text 替换当前字符,直至按下 ESC 键
stext escape 用 text 替换当前字符
S或cctext escape 用 text 替换整行
cwtext escape 将当前字改为 text
Ctext escape 将当前行余下的改为 text
cG escape 修改至文件的末尾

查找与替换

命令 查找与替换动作
/text 从光标处向后查找 text
?text 从光标处向前查找 text
n 在同一方向重复上次查找动作
N 在相反方向上重复上次查找动作
fchar 在当前行向后查找 char
Fchar 在当前行向前查找 char
tchar 在当前行向后查找 char,并定位到 char 之前的一个字符
Tchar 在当前行向前查找 char,并定位到 char 之前的一个字符
:set ic return 设置查找时忽略大小写
:set noic return 设置查找时大小写敏感
:s/oldtext/newtext return 用 newtext 替换掉当前行的 oldtext
:m,ns/oldtext/newtext return 将文件中从 m 行到 n 行中的 oldtext 替换为 newtext
& 重复最后的 :s 命令|
:g/text 查找所有包含 text 的行
:g/text1/s/text2/text3 将所有包含 text1 的行,用 text3 替换 text2
:g/text/commond 在所有包含 text 的行运行 commond 命令(就像上面那个一样)
:v/text/commond 在所有不包含 text 的行运行 commond 命令

复制文本

命令 复制动作
yy 将当前行的内容放入临时缓冲区
nyy 从当前行起,向后将 n 行的内容放入临时缓冲区
p 将临时缓冲区中的内容放入光标之后
P 将临时缓冲区中的内容放入光标之前
“(a-z)nyy 复制 n 行,放入名字为圆括号内的可命名缓冲区,省略 n 代表当前行,不要写括号!
“(a-z)ndd 删除 n 行,并将删除的行放入名字为圆括号内的可命名缓冲区,省略 n 代表当前行
“(a-z)p 将名称为圆括号内的缓冲区内容放入光标之后
“(a-z)P 将名称为圆括号内的缓冲区内容放入光标之前

这里需要注意 学习 vi 命令最重要的是理解其思想,比如前面我们知道,光标向右移动 5 个字符是 5l,那么从光标处拷贝 5 个字符就是 y5l

l 是光标右移命令,5 次 l,表示右移 5 个字符,所以 y5l 就会把当前位置开始的 5 个字符放入缓存。

同理,yfchar 就能理解了,y 是拷贝,fchar 是本行向后查找至 char 字符处,那么 yfchar 就会从光标处一直拷贝到本行第一次出现 char 字符的位置。

vi 的 power 是建立在丰富灵活的命令和强大的 RE 基础上的。掌握了这些才能用好 vi。

撤销与重复

命令 撤销动作
u 撤销最后一次修改
U 撤销 当前行 的所有修改
. 重复最后一次修改
, 以相反的方向重复前面的 f、F、t 或 T 命令
; 以相同的方向重复前面的 f、F、t 或 T 命令
“np 取回最后第 n 次的删除,n 一般小于 9
n 重复前面的 / 或 ? 命令
N 以相反方向重复前面的 / 或 ? 命令

这里应该已经习惯了,也能猜的出来,u 是撤销 最后一次 的更改,那连着撤销 5 次更改呢?当然就是 5u

保存和退出

命令 保存动作
:w 保存但不退出
:w file 另存为 file,且不退出
:wq 或 ZZ 或 :x 保存并退出
:q! 不保存,直接退出
:e! 放弃所有更改,从最后一次保存处开始重新来过

常用 set

选项命令 作用
:set all 打印所有可用的 set
:set nooption 关闭 option 选项
:set nu 显示行号,很有用~
:set showmode 显示当前模式
:set noic 查找时忽略大小写(之前有提到)
:set list 显示制表符和行尾符号
:set ts=8 为文本输入设置 tab stops
:set window=n 文本窗口显示 n 行

感兴趣的可以看看其他的奇妙操作~

查看状态

命令 作用
:.= 打印当前行号
:= 打印文件总行数
^g 显示文件名、当前行号、文件总行数和阅读百分比

放置标记

命令 作用
{ 在第一列插入 { 来定义一个段落
[[ 回到段落的开头处|
]] 向移动到下一个段落的开头处
m(a-z) 用一个字母来表示当前的位置(一样不要写括号啊)
‘(a-z) 回到标记的位置(好用的离谱)

连接

命令 连接动作|
J 将下一行连接到当前行的末尾
nJ 这猜都能猜出来了吧hhh

翻页与屏幕调整

命令 翻页动作
H 将光标移动到 屏幕 的第一行
M 将光标移动到 屏幕 的中间行
L 将光标移动到 屏幕 的倒数第一行
^e 将屏幕向上滚一行
^y 将屏幕向下滚一行
^u 将屏幕向上滚半页
^d 将屏幕向下滚半页
^b 将屏幕向上滚一页
^f 将屏幕向下滚一页
^L 重绘屏幕
zreturn 这是 z 然后敲回车,将当前行置于屏幕的顶行
nzreturn 将第 n 行置于屏幕的顶行
z. 将当前行置于屏幕的中央
nz. 将第 n 行置于屏幕的中央
z- 将当前行置于屏幕的底部
nz- 将第 n 行置于屏幕的底部

这,H、M、L 还有翻页的…就不用再提它可以前面带 n 了吧?

Shell 相关

命令 执行动作
:!commond 执行 shell 的 commond 命令,比如 :!ls
:!! 执行前一次的 shell 命令
:r!commond 读取 shell 命令的执行结果并插入到光标之后
:w!commond 将当前已经编辑的文件,作为 commond 命令的标准输入,并执行 shell 命令
:cd directory 将当前工作目录改为 directory 目录
:sh 启动一个子 shell,用 ^d 返回 vi
:so file 在 shell 程序 file 中读入并执行命令

这个好好用甚至可以玩出花来 hhh

应当避免使用控制键和符号作为宏的触发键!
命令 执行动作
:map key command_seq 定义一个 key,来运行 command_seq 命令,比如 :map euphoria :e!
:map 显示已经定义的宏
:umap key 删除名称为 key 的宏
:ab string1 string2 可以定义一个缩写,当插入 string1 时,自动用 string2 替换。直接键入 string1,然后按 escape,系统就自动插入 string2
:ab 显示所有缩写
:una string 取消 string 的缩写

缩进

命令 缩进动作
^i 或 tab 插入文本时,插入移动的宽度,这个宽度是事先定义好的
:set ai 打开自动缩进
:set sw=n 设置缩进为 n 个字符
<< 将当前行向左移动一个缩进
>> 将当前行向左移动一个缩进|

这里也同理,3>> 就是将下面 3 行向右移动一个缩进。


总结

没有人生下来就会用 vi,这是一个熟能生巧的过程,且绝不是一个死记硬背的过程。

最后再引用一下文中出现的话,vi 的 power 是建立在丰富灵活的命令和强大的 RE 基础上的。掌握了这些才能用好 vi!

-------------本文结束 Euphoria 在此感谢您的阅读-------------

本文标题:vi从入门到入魔

文章作者:王钦弘

发布时间:2020年05月10日 - 00:28

最后更新:2020年05月10日 - 02:37

原始链接:https://www.wqh4u.cn/2020/05/10/vi%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E9%AD%94/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

您的支持将鼓励 Euphoria 继续创作!
(如果你还是学生请千万不要打赏!留点钱在学习上啊!)