
说实话,我刚入行那会儿特别抵触Vim。2015年第一次登录生产服务器,看着那个黑漆漆的终端,我连怎么退出都不知道,硬是按了半天Ctrl+C,最后无奈地关掉了整个终端窗口。现在想想,那时候的自己真是太菜了。
但是干运维这一行,你躲不开Vim。生产环境不可能让你装VS Code,跳板机上也没有图形界面,K8s的Pod里更别想有什么IDE。所以我只能硬着头皮学。
十年过去了,Vim已经成了我身体的一部分。现在编辑配置文件,我的手指比脑子快,经常是改完了才意识到自己做了什么。这种肌肉记忆一旦形成,效率提升不是一点半点。回想起来,这和折腾 技术文档 里的各种配置是一个道理——实践出真知。
我见过太多运维同学,干了好几年还在用nano,或者用Vim但只会i、:wq这几个命令。说实话,这样太浪费了。Vim的设计理念是让你的手指尽量不离开主键盘区,一旦你掌握了它的逻辑,编辑速度能提升5-10倍,这不是夸张。
Vim的技术特点
Vim之所以能在2025年依然是运维工程师的必备技能,有几个核心原因:
无处不在的可用性
几乎所有Linux发行版都预装了vi或vim。不管是CentOS、Ubuntu、Debian,还是Alpine这种精简镜像,你都能找到vi。这意味着不管登录到哪台服务器,你都有一个熟悉的编辑器可用。
SSH友好
Vim是纯终端应用,对网络延迟不敏感。我经常需要SSH到海外的服务器,延迟200-300ms是家常便饭。如果用nano,每敲一个字符都要等半天,但Vim的操作都是本地的,只有保存时才需要网络传输。
强大的批量处理能力
运维工作中经常需要批量修改配置。比如把所有的server_name从旧域名改成新域名,或者在每个server块前面加一行注释。这些操作用Vim的宏和替换功能,几秒钟就能搞定。
可编程性
Vim不只是编辑器,它更像是一门语言。命令可以组合,操作可以重复,逻辑可以封装。你甚至可以用Vimscript写插件,扩展它的功能。
适用场景
作为SRE,我日常使用Vim的场景主要包括:
配置文件编辑
这是最常见的场景。Nginx配置、YAML文件、JSON配置、环境变量文件,这些都需要频繁编辑。Vim的语法高亮和缩进支持,让编辑这些文件变得很舒服。
日志分析
虽然正式的日志分析我会用ELK或者Loki,但快速查看日志文件时,Vim依然是首选。它的搜索功能强大,可以快速定位关键信息。
脚本编写
在服务器上写临时脚本、修改Ansible playbook、调试Shell脚本,这些都离不开Vim。
Git操作
git commit默认会打开Vim让你编辑提交信息,git rebase -i也是用Vim来编辑。如果你不会Vim,这些操作会变得很痛苦。
远程调试
进入K8s Pod排查问题,或者SSH到容器内部,这些环境通常只有vi可用。
环境准备
Vim版本
2025年了,建议至少使用Vim 8.2以上版本,最好是Vim 9.x。新版本带来了很多改进,比如异步任务、终端集成、更好的性能。
检查版本:
vim --version | head -1
操作系统
本文的内容适用于所有主流操作系统,但示例主要基于Linux环境。macOS用户可能需要安装MacVim或通过Homebrew安装最新版Vim。Windows用户建议使用WSL2。
终端要求
建议使用支持256色或真彩色的终端模拟器,比如:
- Linux: Alacritty, Kitty, GNOME Terminal
- macOS: iTerm2, Alacritty
- Windows: Windows Terminal, Alacritty
字体要求
为了获得最佳体验,建议使用等宽字体,最好是Nerd Font系列,这样可以显示文件图标等特殊字符。
安装与验证
在开始之前,确保你的系统安装了完整版的Vim,而不是精简版的vi。
CentOS/RHEL/Rocky Linux:
sudo dnf install vim-enhanced -y
Ubuntu/Debian:
sudo apt update && sudo apt install vim -y
macOS:
brew install vim
验证安装:
vim --version | grep -E "VIM|Included"
确保输出中包含+clipboard、+python3等特性,这些对后续的插件支持很重要。
理解Vim的设计哲学
在深入快捷键之前,我想先聊聊Vim的设计哲学,这对理解后面的内容很重要。
Vim的核心理念是模式化编辑。传统编辑器里,你敲键盘就是输入文字。但Vim不一样,它把"移动光标"、"编辑文本"、"选择文本"这些操作分成了不同的模式。
这样设计的好处是,每个按键都可以有独立的功能。在普通模式下,j是向下移动,d是删除,y是复制。这些操作不需要配合Ctrl或Alt键,直接按就行。这就是为什么Vim用户的效率高——手指不需要离开主键盘区去按那些别扭的组合键。
核心模式详解
普通模式 (Normal Mode)
这是Vim的默认模式,也是最重要的模式。打开Vim时,你就处于普通模式。在这个模式下:
- 字母键用于移动光标或执行命令
- 不能直接输入文本
- 按
i、a、o等键进入插入模式
- 按
:进入命令模式
- 按
v进入可视模式
很多新手的问题就是不理解普通模式。他们打开Vim就开始敲字,结果发现乱七八糟的事情发生了。记住,普通模式是"命令模式",不是"输入模式"。
插入模式 (Insert Mode)
这是输入文本的模式。在这个模式下,Vim的行为和普通编辑器一样,你敲什么就输入什么。
进入插入模式的方式有很多:
i - 在光标前插入
a - 在光标后插入
I - 在行首插入
A - 在行尾插入
o - 在下方新建一行并插入
O - 在上方新建一行并插入
按Esc或Ctrl+[退出插入模式,回到普通模式。
命令模式 (Command Mode)
按:进入命令模式,可以执行各种命令:
:w - 保存
:q - 退出
:wq - 保存并退出
:q! - 强制退出不保存
:%s/old/new/g - 全局替换
可视模式 (Visual Mode)
用于选择文本:
v - 字符选择
V - 行选择
Ctrl+v - 块选择(列选择)
选择后可以进行复制、删除、缩进等操作。
30个核心快捷键详解
好,现在进入正题。我把这30个快捷键分成了几个类别,按照使用频率和重要程度排序。
第一类:移动光标(8个)
这是最基础也是最重要的一类。光标移动效率直接决定了你的整体效率。
1. h, j, k, l - 基础方向移动
这四个键对应左、下、上、右。你可能会问,为什么不用方向键?两个原因:
- 方向键离主键盘区太远,手需要移动
- 这四个键是专门设计的,j像箭头向下,k像箭头向上
刚开始可能不习惯,但强迫自己用一周,就会形成肌肉记忆。
2. w - 跳到下一个单词开头
这是我用得最多的移动命令之一。比hjkl快多了。比如要移动到下一个配置项,按几下w就到了。
server_name example.com;
^ ^ ^
光标位置 按w后 再按w
3. b - 跳到上一个单词开头
w的反向操作。w是word,b是back。
4. e - 跳到当前单词结尾
当你需要在单词末尾添加内容时,ea是一个常用组合。
5. 0 和 $ - 行首和行尾
0跳到行首(第一个字符),$跳到行尾。这两个太常用了,编辑配置文件时几乎每分钟都要用。
另外,^跳到行首第一个非空白字符,在处理有缩进的代码时更实用。
6. gg 和 G - 文件开头和结尾
gg跳到文件第一行,G跳到文件最后一行。查看日志时特别有用,G直接跳到最新的日志条目。
7. Ctrl+d 和 Ctrl+u - 半页滚动
Ctrl+d向下滚动半页,Ctrl+u向上滚动半页。比一行一行按j、k快多了。
8. / 和 ? - 搜索
/向下搜索,?向上搜索。输入关键词后按回车,用n跳到下一个匹配,N跳到上一个匹配。
这个功能在日志分析时太有用了。比如要找所有的error:
/error
然后按n逐个查看。
第二类:编辑操作(10个)
9. i 和 a - 插入
i在光标前插入,a在光标后插入。很多人只知道i,其实a在某些场景下更方便。比如要在某个单词后面加内容,用a就不需要先移动光标了。
10. x - 删除字符
删除光标下的字符,相当于Delete键。小规模删除用这个就够了。
11. dd - 删除整行
这个命令的使用频率极高。删除一行配置,dd搞定。删除多行?5dd删除5行。
12. dw - 删除单词
d是delete操作符,w是移动命令。Vim的设计就是这样:操作符+移动=对移动范围执行操作。
类似的:
d$ - 删除到行尾
d0 - 删除到行首
dG - 删除到文件结尾
13. yy - 复制整行
y是yank(复制),yy复制当前行。5yy复制5行。
14. p 和 P - 粘贴
p在光标后粘贴,P在光标前粘贴。如果复制的是整行,p会在下方粘贴新行。
15. u - 撤销
撤销上一步操作。可以连续按撤销多步。
16. Ctrl+r - 重做
撤销的撤销。我经常撤销过头了,这时候就需要Ctrl+r。
17. . - 重复上一次操作
这是Vim最强大的功能之一。比如你用dd删除了一行,然后移动到另一个位置,按.就会再删除一行。
批量操作时这个功能特别有用。比如要删除多个配置块,删除第一个后,移动到下一个,按.,再移动,再按....
18. >> 和 << - 缩进
>>增加缩进,<<减少缩进。编辑YAML文件时经常用到。
可视模式下选中多行后,按>或<可以批量调整缩进。
第三类:高效组合(7个)
19. ciw - 修改整个单词
c是change,iw是inner word。这个命令会删除光标所在的单词,并进入插入模式。
这是我用得最多的命令之一。比如要把nginx改成apache,光标在nginx任意位置,ciw然后输入apache,完事。
类似的:
ci" - 修改双引号内的内容
ci' - 修改单引号内的内容
ci( - 修改括号内的内容
ci{ - 修改花括号内的内容
20. diw - 删除整个单词
和ciw类似,但不进入插入模式。
21. yiw - 复制整个单词
复制光标所在的整个单词。
22. :%s/old/new/g - 全局替换
这是运维最常用的命令之一。批量替换配置文件中的值:
:%s/192.168.1.100/10.0.0.100/g
各部分含义:
% - 整个文件
s - substitute(替换)
g - global(全局,每行所有匹配都替换)
加上c标志可以逐个确认:
:%s/old/new/gc
23. :n - 跳转到指定行
:100跳转到第100行。查看报错日志时,错误信息通常会告诉你行号,直接跳过去就行。
普通模式下100G或100gg也能达到同样效果。
*24. 和 # - 搜索当前单词*
`向下搜索光标所在单词,#向上搜索。比输入/word`再回车快多了。
25. zz, zt, zb - 屏幕居中
zz - 把当前行移到屏幕中间
zt - 把当前行移到屏幕顶部
zb - 把当前行移到屏幕底部
阅读代码时,把关键行居中显示,看起来更舒服。
第四类:高级技巧(5个)
26. q{register} - 录制宏
这是Vim的杀手锏。宏可以录制一系列操作,然后重复执行。
比如,我有一个配置文件,需要在每行开头加上#注释:
qa - 开始录制,保存到寄存器a
I#<Esc> - 在行首插入#
j - 移动到下一行
q - 停止录制
然后100@a执行100次这个宏。
27. @{register} - 执行宏
@a执行寄存器a中的宏。@@重复执行上一次的宏。
28. m{letter} 和 '{letter} - 标记和跳转
ma在当前位置设置标记a,'a跳回这个标记。
编辑大文件时特别有用。我经常在文件开头的import部分设置一个标记,在主要代码区设置另一个标记,然后在两者之间快速跳转。
29. Ctrl+o 和 Ctrl+i - 跳转历史
Ctrl+o跳到上一个位置,Ctrl+i跳到下一个位置。这个跳转历史是自动记录的,不需要手动设置标记。
30. :!command - 执行外部命令
不用退出Vim就能执行Shell命令:
:!ls -la
:!grep -r "pattern" .
:!kubectl get pods
甚至可以把命令输出插入到当前文件:
:r !date
这会在光标位置插入当前日期。
示例代码和配置
完整的.vimrc配置示例
这是我在2025年使用的.vimrc配置,经过多年迭代优化,既保持了简洁,又包含了运维工作中最实用的功能。
" ============================================================================
" 2025年现代化Vim配置 - SRE运维专用
" 作者:十年SRE老兵
" 更新时间:2025年1月
" ============================================================================
" ---------------------------------------------------------------------------
" 基础设置
" ---------------------------------------------------------------------------
set nocompatible " 关闭vi兼容模式
set encoding=utf-8 " 设置编码
set fileencoding=utf-8 " 文件编码
set fileencodings=utf-8,gbk " 自动识别编码
set termencoding=utf-8 " 终端编码
" 关闭备份文件(运维环境通常不需要)
set nobackup
set nowritebackup
set noswapfile
" 启用文件类型检测
filetype plugin indent on
syntax enable
" ---------------------------------------------------------------------------
" 界面设置
" ---------------------------------------------------------------------------
set number " 显示行号
set relativenumber " 相对行号(方便跳转)
set cursorline " 高亮当前行
set showcmd " 显示命令
set showmode " 显示当前模式
set laststatus=2 " 始终显示状态栏
set ruler " 显示光标位置
set wildmenu " 命令行补全菜单
set wildmode=longest:full,full
" 终端真彩色支持
if has('termguicolors')
set termguicolors
endif
" 配色方案(如果可用)
silent! colorscheme desert
" ---------------------------------------------------------------------------
" 搜索设置
" ---------------------------------------------------------------------------
set hlsearch " 高亮搜索结果
set incsearch " 增量搜索
set ignorecase " 搜索时忽略大小写
set smartcase " 如果有大写则区分大小写
" 按空格清除搜索高亮
nnoremap <Space> :nohlsearch<CR>
" ---------------------------------------------------------------------------
" 缩进设置
" ---------------------------------------------------------------------------
set autoindent " 自动缩进
set smartindent " 智能缩进
set tabstop=4 " Tab宽度
set shiftwidth=4 " 缩进宽度
set softtabstop=4 " 退格时删除4个空格
set expandtab " Tab转空格
" YAML文件使用2空格缩进
autocmd FileType yaml setlocal tabstop=2 shiftwidth=2 softtabstop=2
" Makefile必须使用Tab
autocmd FileType make setlocal noexpandtab
" ---------------------------------------------------------------------------
" 编辑行为
" ---------------------------------------------------------------------------
set backspace=indent,eol,start " 退格键正常工作
set scrolloff=5 " 光标距边缘5行时开始滚动
set sidescrolloff=5 " 水平滚动边距
set wrap " 自动换行
set linebreak " 按单词换行
set mouse=a " 启用鼠标(可选)
set clipboard=unnamedplus " 使用系统剪贴板
set autoread " 文件变化时自动重新加载
" 打开文件时跳转到上次编辑位置
autocmd BufReadPost *
\ if line("'\"") >= 1 && line("'\"") <= line("$") && &ft !~# 'commit'
\ | exe "normal! g`\""
\ | endif
" ---------------------------------------------------------------------------
" 快捷键映射
" ---------------------------------------------------------------------------
" 设置Leader键
let mapleader = ","
" 快速保存
nnoremap <Leader>w :w<CR>
" 快速退出
nnoremap <Leader>q :q<CR>
" 快速保存并退出
nnoremap <Leader>x :wq<CR>
" 分屏导航
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l
" 分屏大小调整
nnoremap <Leader>= <C-w>=
nnoremap <Leader>+ :vertical resize +5<CR>
nnoremap <Leader>- :vertical resize -5<CR>
" 标签页操作
nnoremap <Leader>tn :tabnew<CR>
nnoremap <Leader>tc :tabclose<CR>
nnoremap <Leader>to :tabonly<CR>
nnoremap <Tab> :tabnext<CR>
nnoremap <S-Tab> :tabprev<CR>
" 快速编辑vimrc
nnoremap <Leader>ev :vsplit $MYVIMRC<CR>
nnoremap <Leader>sv :source $MYVIMRC<CR>
" 行移动(类似VS Code的Alt+上下)
nnoremap <A-j> :m .+1<CR>==
nnoremap <A-k> :m .-2<CR>==
vnoremap <A-j> :m '>+1<CR>gv=gv
vnoremap <A-k> :m '<-2<CR>gv=gv
" 保持选中状态缩进
vnoremap < <gv
vnoremap > >gv
" Y复制到行尾(与D、C行为一致)
nnoremap Y y$
" ---------------------------------------------------------------------------
" 性能优化
" ---------------------------------------------------------------------------
set lazyredraw " 宏执行时不重绘
set ttyfast " 快速终端连接
set updatetime=300 " 更新时间(毫秒)
set timeoutlen=500 " 映射超时
set ttimeoutlen=10 " 键码超时
" 大文件优化(禁用某些功能)
autocmd BufReadPre * if getfsize(expand("%")) > 10000000 |
\ set eventignore+=FileType |
\ setlocal noswapfile bufhidden=unload buftype=nowrite undolevels=-1 |
\ endif
" ---------------------------------------------------------------------------
" 插件管理(vim-plug)
" ---------------------------------------------------------------------------
" 自动安装vim-plug
let data_dir = has('nvim') ? stdpath('data') . '/site' : '~/.vim'
if empty(glob(data_dir . '/autoload/plug.vim'))
silent execute '!curl -fLo '.data_dir.'/autoload/plug.vim --create-dirs
\ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
endif
" 插件列表
call plug#begin('~/.vim/plugged')
" 文件浏览器
Plug 'preservim/nerdtree'
" 模糊搜索
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'
" Git集成
Plug 'tpope/vim-fugitive'
Plug 'airblade/vim-gitgutter'
" 语法高亮增强
Plug 'sheerun/vim-polyglot'
" 注释工具
Plug 'tpope/vim-commentary'
" 包围符号操作
Plug 'tpope/vim-surround'
" 自动补全括号
Plug 'jiangmiao/auto-pairs'
" 状态栏美化
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
" 配色方案
Plug 'morhetz/gruvbox'
call plug#end()
实际应用案例
案例1:批量修改Nginx配置
场景:需要把所有upstream中的服务器IP从192.168.1.x改成10.0.0.x
upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}
upstream cache {
server 192.168.1.201:6379;
server 192.168.1.202:6379;
}
解决方案:
:%s/192\.168\.1\./10.0.0./g
注意点号需要转义,因为在正则表达式中点号匹配任意字符。
执行结果预览:
:%s/192\.168\.1\./10.0.0./gc
加上c标志可以逐个确认,更安全。
案例2:批量添加注释
场景:需要临时禁用一段配置
方法1:使用可视块模式
- 移动到第一行需要注释的位置
Ctrl+v进入块选择模式
j向下选择多行
I#在每行开头插入#
Esc完成
方法2:使用宏
qa - 开始录制
I# <Esc>j - 注释当前行并移动到下一行
q - 停止录制
10@a - 对接下来10行执行相同操作
方法3:使用vim-commentary插件
V进入行选择模式
- 选中需要注释的行
gc一键注释
案例3:处理YAML文件缩进
Kubernetes的YAML文件对缩进要求严格,经常需要调整。
场景:需要把一段配置移动到另一个层级
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
操作:
V选中需要调整的行
>增加缩进,或<减少缩进
- 可以用
.重复缩进操作
技巧:选中后按3>可以一次增加3级缩进。
案例4:查看和分析日志
场景:在一个大日志文件中查找错误
" 打开日志文件
vim /var/log/nginx/error.log
" 跳到文件末尾(最新日志)
G
" 搜索error
/error
" 跳转到下一个匹配
n
" 跳转到上一个匹配
N
" 只显示包含error的行
:g/error/p
" 删除所有包含某模式的行(比如清理调试日志)
:g/DEBUG/d
" 保留只包含error的行,删除其他
:v/error/d
案例5:处理JSON配置
场景:需要编辑一个压缩的JSON文件
" 格式化JSON(需要安装jq)
:%!jq .
" 或者使用Python
:%!python3 -m json.tool
" 压缩JSON(去除空格换行)
:%!jq -c .
我的配置中已经映射了<Leader>jq来快速格式化。
故障排查和监控
日志查看技巧
实时查看日志
虽然Vim不是实时查看日志的最佳工具,但可以这样操作:
" 打开日志文件
vim /var/log/nginx/access.log
" 跳到末尾
G
" 手动刷新
:e
更好的方案是用tail -f或less +F,但如果需要编辑和查看结合,Vim也能用。
日志过滤
" 只显示包含特定模式的行
:g/pattern/p
" 删除所有匹配的行
:g/pattern/d
" 删除所有不匹配的行(保留匹配的)
:v/pattern/d
" 统计匹配行数
:%s/pattern//gn
问题排查清单
当Vim行为异常时,按这个清单排查:
1. 检查Vim版本
vim --version
2. 检查加载的配置文件
:version
:scriptnames
3. 检查当前设置
:set all
:set " 只显示非默认值
4. 检查映射
:map
:imap
:nmap
5. 使用干净模式启动
vim -u NONE -N
这会跳过所有配置,如果问题消失,说明是配置导致的。
总结
要点回顾
核心理念
- Vim是模式化编辑器,理解模式是第一步
- 操作符+动作=命令,这是Vim语法的核心
- 肌肉记忆需要时间形成,但一旦形成就是一辈子的技能
最重要的10个快捷键
i - 进入插入模式
Esc - 回到普通模式
:wq - 保存退出
dd - 删除行
yy / p - 复制粘贴行
/ - 搜索
u - 撤销
. - 重复上次操作
:%s/old/new/g - 全局替换
ciw - 修改单词
运维必备技能
- 熟练使用搜索和替换
- 掌握可视块模式处理列操作
- 会录制和执行宏
- 能够分屏编辑多个文件
进阶方向
第一阶段:基础巩固(1-2周)
- 强迫自己不用方向键,只用hjkl
- 熟练使用w、b、e进行单词级移动
- 掌握基本的编辑操作
第二阶段:效率提升(2-4周)
- 学习文本对象(iw、i"、i{等)
- 掌握宏的录制和执行
- 配置个人.vimrc
第三阶段:高级技巧(1-3个月)
- 安装和配置插件
- 学习Vimscript基础
- 掌握更多的命令和技巧
在云栈社区,我们经常探讨这类运维/DevOps/SRE工具的使用心得,欢迎来分享你的Vim故事。
附录:命令速查表
| 移动命令 |
命令 |
说明 |
| h j k l |
左 下 上 右 |
| w / W |
下一个单词开头 |
| b / B |
上一个单词开头 |
| 0 |
行首 |
| ^ |
行首非空字符 |
| $ |
行尾 |
| gg |
文件开头 |
| G |
文件结尾 |
| Ctrl+d |
向下半页 |
| Ctrl+u |
向上半页 |
| 编辑命令 |
命令 |
说明 |
| i / I |
在光标前/行首插入 |
| a / A |
在光标后/行尾插入 |
| x |
删除字符 |
| dd |
删除行 |
| yy / Y |
复制行 |
| p / P |
在后/前粘贴 |
| u / Ctrl+r |
撤销/重做 |
| . |
重复上次操作 |
| >> / << |
增加/减少缩进 |
| 搜索替换 |
命令 |
说明 |
| /pattern |
向下搜索 |
| ?pattern |
向上搜索 |
| n / N |
下一个/上一个匹配 |
| * / # |
搜索当前单词 |
| :%s/old/new/g |
替换全文所有 |
| :%s/old/new/gc |
替换全文(确认) |
| 宏操作 |
命令 |
说明 |
| qa |
开始录制到寄存器a |
| q |
停止录制 |
| @a |
执行寄存器a的宏 |
| @@ |
重复上次宏 |
| 10@a |
执行宏10次 |
| 文本对象 |
命令 |
说明 |
| iw / aw |
单词(inner/a word) |
| i" / a" |
双引号内/含引号 |
| i( / a( |
括号内/含括号 |
| i{ / a{ |
花括号内/含花括号 |