1. 为什么vi是Linux绕不过去的编辑器
先说个我自己的经历。早些年接手一台跑着老版本CentOS的服务器,SSH登上去准备改个nginx配置,下意识敲了vim,系统直接提示command not found。再试nano、emacs,全都没有。最后发现系统里唯一躺着的编辑器就是vi,而且没装任何额外软件包的环境根本不允许我随便yum install。那一刻我意识到:vi不是一个"可选"的编辑器,而是Linux系统的保底技能。
这个场景放到今天依然成立。无论是Docker容器里精简到极致的镜像、嵌入式开发板上的BusyBox环境,还是刚装完的最小化Linux系统,vi几乎是唯一被预装的文本编辑器。所以它既是面试题里的常客,也是生产环境里最后一道兜底。网上经常有人争论"vi该不该学"、"都用IDE了为什么还要用vi",我的观点很直接:你可以不用vi写代码,但你不能保证每台机器上都有你习惯的工具。
把vi练熟最大的价值在于:它不依赖图形界面、不依赖网络、不依赖额外的软件包,只要有一个终端、一个SSH连接,就能完成几乎所有文本编辑工作。这篇文章我把vi从打开文件到批量编辑、从基本操作到配置优化的完整脉络捋一遍,尽量用实际操作的写法来讲,而不是堆命令列表。适合刚接触Linux的新手,也适合已经会用但一直没系统梳理过vi操作的老手。
2. vi与vim的区别,以及三种模式
2.1 vi和vim到底是什么关系
很多新手会困惑:vi和vim到底是不是同一个东西?简单说,vim是vi的增强版,全称是Vi IMproved。vim在vi的基础上增加了语法高亮、多级撤销、分屏、插件扩展这些现代功能,而vi本身是几十年前UNIX时代就存在的经典编辑器。vim的启动命令里往往有个vi的软链接,所以你在终端里敲vi,实际打开的可能是vim。这也是为什么很多人觉得vi也能显示颜色,其实那是vim在后台工作。
判断当前用的到底是vi还是vim,看两个地方:输入:version看看输出内容,或者看编辑器底部的版本提示。如果是纯粹的vi,命令模式下输入一些vim专属命令时会报错。日常学习中,建议直接把vi和vim当成同一套操作体系来练,所有vi能用的按键操作,vim全部兼容。反过来vim多出来的高级功能,在实际服务器操作中反而是加分项。
2.2 三种模式的切换,是vi的精髓
vi和普通编辑器最大的不同是它有模式。Windows记事本也好、macOS的TextEdit也罢,你打开就能直接打字,vi不行。vi有三种基础模式,理解它们,vi就理解了80%。
- 命令模式(Normal mode):打开vi后默认进入的模式。这个模式下所有按键都是命令,比如
j是向下移动、x是删除当前字符、dd是删除整行。 - 插入模式(Insert mode):在这个模式下才能像普通编辑器一样输入文字。从命令模式按
i进入。 - 底行模式(Last line mode):也叫命令行的扩展模式,按
:进入,用于保存退出、查找替换、设置参数等下指令操作。
初学者最容易犯的错误是:不知道当前处于什么模式,直接乱敲键盘。比如在命令模式下想输入数字,屏幕上却跳来跳去;或者按了i进入插入模式后,以为还在命令模式,想保存文件,结果把:当字符输入了。记住一个口诀:按完命令先看一眼左下角,显示INSERT说明在插入模式,空白的说明在命令模式。
三种模式切换的关系可以这么记:打开vi默认是命令模式,按a或i进入插入模式,按Esc或者Ctrl+C退回命令模式,在命令模式按:进入底行模式,执行完底行命令后自动回到命令模式。
3. 核心操作实录:从打开文件到保存退出
3.1 打开文件的方式与场景选择
打开一个文件最简单的方式就是vi 文件名。但实际工作中会面对各种具体情况:
# 打开现有文件,光标定位到第一行 vi /etc/nginx/nginx.conf # 打开文件并定位到第50行 vi +50 /var/log/messages # 打开文件并定位到包含"error"的第一个匹配处 vi +/error /var/log/messages # 文件不存在时,vi会创建新文件,保存后才有名字 vi newfile.txt # 以只读方式打开,防止手滑 vi -R important.txt第2个和第3个参数特别实用。排查日志文件时,日志动辄几千行,打开后光标直接停在指定的行或关键字上,能省不少上下翻找的时间。我曾经排查一个应用启动失败的日志,就用vi +/ERROR app.log直接跳到报错位置,效率比一条条翻高得多。
3.2 光标移动:别再用方向键了
新手阶段用方向键移动光标完全没问题,但熟练以后一定要养成用hjkl的习惯。原因是方向键离字母区远,手要离开主键盘区,效率低,而且有些精简环境或者SSH终端对方向键支持不好。hjkl四个键的对应关系:h向左、j向下、k向上、l向右。
进阶一点的移动命令更实用:
w向后跳一个单词,b向前跳一个单词0跳到行首,$跳到行尾gg跳到文件第一行,G跳到文件最后一行Ctrl+F向下翻一页,Ctrl+B向上翻一页Ctrl+D向下翻半页,Ctrl+U向上翻半页H跳到屏幕顶部,M跳到屏幕中间,L跳到屏幕底部
这里说一下我的实操心得:在日志里定位问题,用/关键词搜索比手动翻页快得多。vi里的搜索在命令模式下直接输入/加上要搜的内容,回车后按n继续向后找,按N向前找。搜索支持正则表达式,比如/^ERROR能匹配每行开头的ERROR字样,/ERROR.*timeout能匹配包含ERROR和timeout的行。
3.3 插入文本:i、a、o的区别
进入插入模式的方式不止i一种,不同场景下用不同按键能减少后续的光标移动操作。
i:当前光标所在字符前面插入,最常用a:当前光标所在字符后面插入I:跳到当前行的行首插入A:跳到当前行的行尾插入,这个非常常用,比如在配置文件的某行末尾加分号o:在下方新建一行并进入插入模式O:在上方新建一行并进入插入模式
写一个实际例子:改nginx配置时,想把listen 80;改成listen 8080;,光标移到80后面按r替换或者按x删掉再按i插入,用A直接跳行尾追加反而绕路了。而如果要在某个块下面追加一行配置,用o最顺手。这些选择本质上是减少光标移动次数,练得越熟,日常改配置的节奏感就越强。
3.4 删除、复制、粘贴与撤销
vi里的删除和复制不是通过鼠标,而是通过命令组合。这里的核心概念是操作对象:按什么键,作用于哪个范围。
x:删除光标所在字符(相当于Windows记事本里的Delete键)X:删除光标前面的字符(相当于退格键)dd:删除光标所在整行5dd:删除从当前行开始的5行dw:删除一个单词d$:从光标位置删除到行尾d0:从光标位置删除到行首yy:复制光标所在行5yy:复制从当前行开始的5行y$:从光标位置复制到行尾p:粘贴到当前行下方(复制的是整行时)P:粘贴到当前行上方
撤销和恢复同样重要:u撤销上一步操作,Ctrl+R恢复刚才撤销的操作。注意,u和Ctrl+R是一对逆操作,如果撤销撤多了,用Ctrl+R找回来。vi默认只支持一级撤销(旧的vi),但vim支持多级撤销,这也是vim比vi好用的地方之一。
还有一个命令容易被忽略:J把当前行和下一行合并成一行,处理换行混乱的文件时很有用。
3.5 保存退出的正确姿势
保存退出是vi里最常见的操作,也是最容易出问题的操作。底行模式下几个关键命令:
:w:保存文件,不退出:q:退出(没有修改时):q!:不保存修改,强制退出:wq:保存并退出:x:保存并退出(与:wq等价,但:x只在有修改时才写入文件)ZZ:命令模式下保存并退出,注意这个没有冒号:w 新文件名:另存为:wq!:强制保存并退出(文件只读时用)
重点讲讲权限问题。有时候你打开一个系统配置文件,改了保存时报E212: Can't open file for writing,这就是当前用户没有写权限。两个处理方式:用sudo vi重新打开文件,或者在vi里用:w !sudo tee %这个经典技巧——它通过shell命令把当前buffer内容写回文件。这个命令我实际用过的场景:不小心用普通用户打开了/etc/hosts,改完才发现没权限保存,又不想退出重来,就用它救场。不过提醒一句:生产环境改系统配置前,最好先备份,任意编辑器操作都有手误的风险。
4. 批量操作:查找替换与多文件处理
4.1 查找替换的完整语法
工作中很多配置文件的批量修改,比如把所有http://改成https://,手动一条条改效率太低,必须用vi的替换命令。替换命令的基本格式是:
:范围 s/查找内容/替换内容/标志范围常用的几种写法:
%s表示全文件,%是每一行的意思5,10s表示第5行到第10行.,+10s表示从当前行到下面10行.,$s表示从当前行到文件末尾
标志部分:
g:替换该行所有匹配,而不是每行只替第一个——这个必须加,不加的话每行只替换第一个匹配,容易漏c:每次替换前询问确认,安全操作推荐加i:忽略大小写
实际示例:
# 全文件把所有old替换成new,每行所有匹配都替换 :%s/old/new/g # 全文件把所有old替换成new,每条都要确认 :%s/old/new/gc # 只替换第10到20行 :10,20s/old/new/g # 忽略大小写替换 :%s/old/new/gic标志的交互很多人第一次见会比较懵:执行后会逐个显示匹配位置,问replace with new? (y/n/a/q/l/e)。y替换,n跳过,a全部替换剩余,q退出,l替换本次并退出,e等价于q但报错信息不同。我的建议是:改动量大、规则又没那么明确时,先用gc试运行一遍,看几个实际上下文再决定是否全量替换。我以前改数据库连接串时就吃过亏:配置文件里有注释和实际生效两处都含旧地址,用g直接全部替换,注释里也被改了,虽然不影响运行,但给后来排查的人造成了困惑。
4.2 多文件编辑与切换
vi打开多个文件的方式:
vi file1.txt file2.txt file3.txt:n切换到下一个文件,:N或:prev切换到上一个文件。:files列出所有打开的文件,每行前面有%的表示当前文件,有#的表示上一个文件。:e 文件名可以在不退出vi的情况下打开另一个文件到当前buffer。
用vim还能分屏操作::sp水平分屏,:vsp垂直分屏,Ctrl+W加方向键在分屏间跳转。调试配置文件对比时这个功能很好用,左边放原文件,右边放要改的文件,边看边改。不过分屏功能要求vim,老系统中的纯vi不支持,用之前先确认环境里是vi还是vim。
多文件编辑我对新手的建议是先别深入,知道有这功能即可。真正经常用到的是vi /etc/nginx/nginx.conf然后配合nginx -t测试语法,而不是在vi里开一堆文件来回切换。
4.3 寄存器和宏:重复操作的加速器
vi有个"寄存器"的概念,可以用"ayy把当前行复制到a寄存器(而不是默认的匿名寄存器),然后用"ap把它粘贴出来。这个有什么用?比如你需要在不同地方多次粘贴不同的内容块,默认粘贴会覆盖上一次的内容,寄存器可以同时存多份,互不干扰。
更强大的是宏录制。命令模式下按q再按一个字母(比如a),开始把后续所有操作录制到a寄存器,再按q停止。之后想重复这套操作,在命令模式下按@a执行一次,@@重复执行上一次宏命令。宏让我最惊艳的一次是批量格式化一个几百行的配置文件:手动完成一行中"删除多余空格→移动字段位置→在行尾加逗号"三步操作,录成宏,然后用99@a一次性处理完剩余行。前提是每行结构一致,否则宏会跑飞,操作前最好找几行做测试。
5. 配置与优化:让vi更像一个现代编辑器
5.1 临时配置与配置文件
vi的核心逻辑是"默认保守、按需开启"。在底行模式可以临时设置,比如:
:set nu显示行号,:set nonu取消:set list显示不可见字符(比如行尾的$、Tab的^I),检查混入空格还是Tab时很有用:set expandtab让Tab键展开成空格(Python用户强烈建议开):set ts=4 sw=4设置Tab宽度为4空格:set hlsearch高亮搜索匹配结果:set incsearch边输入边搜索
这些设置如果每次都要手动敲,太烦了,所以要把它们写进vi的配置文件:/etc/vimrc(系统级)或者~/.vimrc(用户级)。新建~/.vimrc,写入这几行基本不会出错:
set nu " 显示行号 set expandtab " Tab键转空格 set ts=4 " Tab宽度4 set sw=4 " 缩进宽度4 set hlsearch " 搜索高亮 set incsearch " 增量搜索 set cursorline " 高亮当前行 syntax on " 语法高亮配置文件生效方式是重启vi或者底行执行:so ~/.vimrc重新加载。不写set compatible是因为vi和vim的配置默认就不同,保持vim默认行为即可。
5.2 编码问题与乱码处理
服务器上编辑文件,经常会遇到中文乱码——文件是用UTF-8还是GBK编码,会影响vi的显示。vi底行模式执行:set fileencoding可以查看当前文件编码。如果是乱码,先试:set encoding=utf-8再:set fileencoding=utf-8,然后:w保存。如果文件本身是GBK,试:set fileencoding=gbk。实在不行用iconv命令转换编码:
iconv -f gbk -t utf-8 oldfile.txt > newfile.txt从经验来说,Linux环境下产生的文件基本是UTF-8,乱码多半是文件从Windows传过来的(Windows老系统默认GBK),或者终端编码设置不对。vi本身能正确识别UTF-8,但终端如果设置成别的字符集,显示照样乱。这种情况先排查LANG环境变量和SSH客户端的字符集,再怪vi不迟。
5.3 恢复意外中断未保存的文件
vi有个特性常被忽略:它会在编辑时创建一个临时交换文件,文件名是文件名.swp。如果编辑过程中SSH断线、终端被强杀,vi会保留这个swap文件,再次打开原文件时会提示Swap file "xxx.swp" already exists并提供恢复选项。
遇到这种情况,按R(Recover)即可恢复上次未保存的内容,恢复后记得删除swap文件。这个功能我救过命:有次在服务器上改一个很长的配置脚本,改到一半公司断电,所有终端都断开了,当时没保存,心态崩溃。重新连接后发现swap文件还在,恢复后内容几乎都在,只少了最后几行。重要文件编辑到关键节点,按一下:w不算浪费,一两秒的代价远小于从头再来。
vim -r 文件名也能查看有没有可恢复的版本。清理swap文件的命令是rm 文件名.swp,但谨慎操作,确认不需要恢复再删,删了就真没了。
6. 进阶技巧:块操作、标签页与远程文件
6.1 块选模式:批量处理多行内容和列编辑
vi里有个叫"可视块"的功能,其他编辑器里叫"列模式",快捷键是Ctrl+V。进入后按方向键选出一个矩形区域,可以对这个区域统一操作。典型的场景:
- 多行行首统一加注释:光标移到第一行行首,
Ctrl+V向下选到最后一个目标行,按Shift+I输入#,再按Esc,所有选中的行首都会插上#。 - 多行统一删除:选中几行的行首字符,按
d,批量删掉。 - 多行替换:选中区域后输入
:s/old/new/g,在选中的多行内做替换。
这个功能在处理配置文件的批量注释时效率极高。比如nginx里临时屏蔽一组server块,或者批量给几十行日志分析脚本加注释,块操作比一行行改快太多了。注意:块选模式下,Shift+I插入的内容会在按Esc后生效,这是很多人卡住的地方,插入了半天没反应,急得猛按键盘,其实是没按Esc确认。
6.2 标签页:多个文件的分组管理
现代vim和部分vi发行版支持标签页,类似浏览器的标签。:tabnew 文件名在新标签打开文件,:tabnext切到下一个标签,:tabprev切到上一个,:tabclose关闭当前标签。这个功能适合在多个相关文件中切换,比如同时改应用的前后端代码、或者对比MySQL配置和服务日志。不过标签页是vim特性,纯vi环境没有,优先级排在块操作之后。
实际项目中我更常用分屏而不是标签页::vsp左右分屏,左屏打开配置文件,右屏打开日志文件,边改边看报错;:sp上下分屏,适合上面是文件头注释、下面是正文的场景。分屏比标签页好在两个文件同时可见,不用来回切换,对照效率更高。
6.3 在vi中直接执行Shell命令
vi的底行模式可以透出shell命令,用!前缀。这个功能使用频率不算高,但关键时刻能省很多事。:!ls在vi里执行ls并显示结果(按回车回到vi),:%!sort把当前文件所有行用sort命令排序后放回文件,:%!grep -v "^#"把不包含注释的行过滤回文件。
比较实用的是配合tee类命令做权限补救,前面提过的:w !sudo tee %就是例子。还有一些系统管理场景:在vi里直接跑:%!python3 -m json.tool对JSON格式进行格式化和纠正,不用退出编辑器到命令行去操作。底行模式的!是把当前文件内容作为标准输入传给外部命令,注意是整行替换的语义,不理解的先在小文件上试一次再用于真实文件。
7. 常见问题与排查技巧实录
7.1 方向键和退格键不好使
新装的Linux、或者某些终端模拟器下,vi(特别是纯vi)对于方向键的映射不完善,按方向键会输出A、B、C、D或者出现^[[A之类的字符。这其实就是终端和编辑器对键盘序列的解析不匹配。
处理方式:一是用hjkl代替方向键,这在vi里最稳;二是检查终端类型,echo $TERM,如果显示的是dumb之类的,vi很多按键功能都会异常,试着把终端类型设置成xterm-256color或xterm;三是退格键不好使时,可以在vi的配置文件里加set backspace=indent,eol,start,这个配置项的作用是允许退格键删除缩进、行尾、以及之前插入的内容,默认设置为空的时候,退格键在某些模式下最多只能删到插入点,给用户一种"退格键没反应"的错觉。
7.2 文件总是显示只读
vi编辑文件时提示只读,先确认文件权限:ls -l 文件名。如果权限确实是644(当前用户无写权限),用sudo vi重新打开;如果权限没问题,考虑是不是设了immutable属性:lsattr 文件名,如果显示i属性,需要用chattr -i解除,这属于高级场景,一般出现在安全加固过的系统上。还有一层可能:文件本身在一个只读挂载的分区上,用mount | grep 路径查看。
注意一点:如果文件有写权限,vi是允许编辑只读文件的(只是保存时提示确认),vi的"只读"是广义的。如果你只是在vim里按了Ctrl-X Ctrl-Q之类的组合,进入了某种只读模式,按Esc退出再试试普通编辑就行。
7.3 大文件编辑卡顿与性能优化
日志文件动不动几百MB甚至GB级别,直接用vi打开会非常卡,甚至卡死。我的经验是:
# 只查看文件尾部几百行 tail -n 200 /var/log/syslog # 按关键字过滤 grep "error" large.log | vi - # 如果只需要修改,先split split -l 10000 large.log # 用sed流编辑 sed -i 's/old/new/g' large.logvi适合编辑配置文件这类中小文本,不适合当大日志浏览器。如果需要精确查看大文件的部分区域,:e配合地址定位(比如:e +50000 large.log)可以跳到第5万行,但这只是应急办法。真正处理大文件,应该用less浏览、grep过滤、sed批量替换,vi只在需要精准编辑时登场。
7.4 误操作求救:从死循环到意外退出
新手在vi里最常遇到的"事故"是:不知道把自己弄到了什么状态,屏幕上的字还在跳。这里给一套紧急脱险流程:
- 按多次
Esc:无论什么模式,先回命令模式,这个操作无副作用,放心按。 Ctrl+C:同样可以中断当前操作并回到命令模式,但如果正在编辑,可能中断宏或命令,谨慎用。- 如果屏幕底部有个
:后跟着命令,不想执行就删掉或者按Esc取消。 - 如果屏幕显示
Record宏名之类的,再按q结束录制。 - 如果完全是乱的,按
q!并直接回车强制退出不保存,重新打开文件再来一次,比在混乱状态里硬编安全得多。
我见过有人把vi的文件内容改得一团糟,不知道是误按了什么替换命令,想撤销又撤销不动,最后直接q!退出重来。真相是:如果做过比较多步骤的修改,u只能一步一步撤销,远不如接受损失重来一次干净。所以重要文件修改前,先cp备份是好习惯。
7.5 面试题速查:有哪些高频考点
vi在Linux面试题中出现的频率非常高,整理几个常见考点:
- vi有哪几种模式?怎么切换?
- 怎么在vi里查找和替换字符串?
- 如何撤销操作?如何恢复撤销?
- 如何跳到文件的第N行?(
:N底行定位,或者NG) - 怎么把文件另存为?(
:w 新文件名) - 一个文件被编辑到一半断线了,怎么恢复?(swap文件)
- 如何在vi中删除从当前行到文件末尾的所有内容?(
:.,$d)
面试不一定要考到特别难的操作,主要看基础掌握程度,三板斧(模式切换、保存退出、查找替换)背熟了就能过。但如果面试时用vi写个脚本,把行号、搜索、替换、寄存器这些实际操作拿出来,就能明显和其他候选人拉开差距。建议自己在开发机上用真实文件把文章里的操作练两遍,肌肉记忆才是面试能发挥出来的底气。
8. 编辑技巧的迁移与扩展
vim的强大不仅在于内置命令,还在于它是一套可扩展的基础工具。学完vi基础后,很多人会沿着两条路线深入:一是学习vim的插件体系,比如Vundle或vim-plug管理插件、coc.nvim做代码补全、NERDTree做文件树、fzf做模糊查找、vim-airline做状态栏美化;二是把vi的按键习惯迁移到其他工具,最典型的就是浏览器里用Vimium插件让网页也能用vi风格快捷键操作,以及IDE里装vim模拟插件(比如VSCode的VSCodeVim、JetBrains的IdeaVim)。
我的建议是先把原生vi操作练扎实,再去碰插件。插件能提升体验,但不能替代基础能力。你总会遇到一台机器既没有插件也没有高配vim,甚至没有vim只有vi,到那时候,脑子里存着的命令和手指上的肌肉记忆才是真正的生产力工具。
vimrc配置文件的颗粒度也可以根据自己的习惯慢慢调整。比如经常写Python的人,单独把缩进设成4空格并加set autoindent;经常写Shell脚本的人,可能想关掉语法高亮或者换成更适合自己的配色。配置是个人化的,参考别人的dotfiles项目可以,但先理解每行配置的含义,再决定要不要用,光抄配置而不懂配置,出了问题很难排查。
最后说一个可以立刻做的事:打开终端敲vi --version(或者vim --version)看看你的环境支持哪些特性,然后新建一个临时文件,把文中的命令从1到7过一遍。别看,直接上手敲。你会发现自己比想象中更快适应vi的节奏。
实际用vi的时间越长,越能体会到一个道理:工具的价值不在于它有多酷,而在于它是否有足够深的底线。很多编辑器在图形界面时代被吹得天花乱坠,真正到了生产现场、SSH窗口、纯文本环境,能稳定输出的还是那些最朴素的功能。vi是一个"难学但值得学"的典型:初期的挫败感换来的是未来无数场景下的从容,这笔账,怎么算都不亏。