Vim 与 Shell 协作:挂起、恢复与外部命令

为什么要学这一部分

平时把 Vim 当作编辑器使用时,很容易形成这样的习惯:退出 Vim,执行一条 Shell 命令,再重新打开文件。实际上,Vim 和 Shell 之间有多种协作方式。学会后,可以在不丢失当前缓冲区、光标位置、窗口布局和撤销历史的情况下,临时回到 Shell,或者直接让外部命令处理当前文本。

这一部分最值得先掌握的不是大量命令,而是三个层次:

  1. 暂时离开 Vim,再回到原来的编辑现场;
  2. 在 Vim 内执行一条或一组 Shell 命令;
  3. 把文本送给外部命令处理,再把结果带回 Vim。

先理解:Ctrl-Z 不是退出 Vim

在 Linux、macOS、WSL 等支持作业控制的 Shell 中,Vim 通常是 Shell 的一个前台作业:

Shell 启动 Vim

Vim 在前台运行
↓ Ctrl-Z
Vim 被暂停,Shell 回到前台
↓ fg
原来的 Vim 恢复运行

在 Vim 的普通模式中按 Ctrl-Z,通常相当于执行:

:suspend

有些资料也写成:

:stop

此时 Vim 进程仍然存在,但它处于 Stopped(暂停) 状态。缓冲区、光标位置、分屏和撤销历史都还在内存中。

回到 Shell 后,最常用的是:

jobs          # 查看当前 Shell 管理的作业
fg # 恢复最近的作业到前台
fg %1 # 恢复编号为 1 的作业

一个完整练习如下:

vim demo.txt

在 Vim 中做一点编辑,先执行 :w,然后在普通模式按 Ctrl-Z。回到 Shell 后执行:

jobs -l
fg

Vim 会回到刚才的位置。

为什么通常不要对 Vim 使用 bg

bg %1 的含义是让已暂停的作业在后台继续运行。然而交互式 Vim 需要读取终端输入;后台进程尝试读取终端时,通常会再次被 Shell 暂停。因此对 Vim 来说,最自然的组合是:

Ctrl-Z → 在 Shell 中做事 → fg

而不是:

Ctrl-Z → bg

另外,暂停不等于持久化。关闭终端、Shell 退出或系统异常时,暂停的 Vim 仍可能丢失,所以养成挂起前 :w 的习惯更稳妥。需要跨终端长期保留会话时,应考虑 tmuxscreen 或 Vim 的会话文件,而不是依赖 Ctrl-Z

在 Vim 中执行一条 Shell 命令::!

如果只想执行一条命令,没有必要挂起 Vim:

:!pwd
:!ls
:!git status
:!python3 current_script.py

命令结束后按回车即可回到 Vim。适合查看目录、Git 状态、脚本输出等短任务。

% 在 Vim 命令行中可代表当前文件名,例如:

:!python3 %

如果完整路径可能包含空格,更稳妥的写法是:

:execute '!python3 ' . shellescape(expand('%:p'))

其中 expand('%:p') 得到当前文件的绝对路径,shellescape() 负责按当前 Shell 的规则转义。

临时进入一个子 Shell::shell

如果要连续执行多条命令,可以在 Vim 中输入:

:shell

简写为:

:sh

这会从 Vim 启动一个新的子 Shell。在子 Shell 中完成操作后,输入:

exit

即可回到 Vim。

需要区分两种方式:

  • Ctrl-Z:暂停 Vim,回到启动 Vim 的原 Shell;
  • :shell:Vim 保持运行,并启动一个新的子 Shell。

如果只是回去执行一两条已有历史命令,我更偏向 Ctrl-Z;如果希望在 Vim 内临时开一段独立的 Shell 工作流,则使用 :shell

让 Shell 输出进入当前文件::read !

外部命令的输出可以直接插入光标所在行的下一行:

:read !date
:read !pwd
:read !ls

简写为:

:r !date

这适合插入时间、目录列表、生成的配置片段或脚本结果。若要把输出插到文件第一行之前,可以使用:

:0read !date

把 Vim 文本交给 Shell::write !

下面的命令不会把文件保存为名叫 wc 的文件,而是把当前缓冲区内容作为标准输入交给 wc -l

:write !wc -l

也可以先在可视模式选中几行,再执行:

:'<,'>write !wc -l

这类操作适合统计、检查或把一段文本送给外部脚本,但不修改当前缓冲区。

使用外部命令直接过滤文本::! 与范围

! 前面带有行范围时,Vim 会把这些行交给外部命令,并用命令输出替换原文本。

对整个文件排序:

:%!sort

排序并去重:

:%!sort -u

格式化 JSON:

:%!python3 -m json.tool

也可以在可视模式选中一部分文本后按 !,输入 sort 并回车,只处理选中区域。

这种操作会修改缓冲区。如果结果不符合预期,立即按 u 撤销即可。第一次练习时最好使用临时文件,并先保存一次。

编译与搜索:让结果进入 quickfix

直接运行 :!make 可以看到输出,但错误位置只是普通文本。使用:

:make
:copen

Vim 会按照 makeprgerrorformat 解析编译信息,并把错误放入 quickfix 列表。随后可以使用:

:cnext
:cprevious
:cclose

在错误之间跳转。

搜索也可以采用类似工作流:

:grep TODO -R .
:copen

这比在 Vim 外运行命令、手工记住文件名和行号更适合代码阅读。

内置终端::terminal

支持 +terminal 功能的 Vim 可以直接打开终端缓冲区:

:terminal

也可以在分屏中打开:

:belowright split | terminal

终端正在接收输入时,可以按 Ctrl-W N 进入 Terminal-Normal 模式,再用 Vim 的窗口命令移动或复制内容;按 i 可重新把输入交给终端程序。

内置终端适合需要持续观察的命令、REPL 或测试进程。只执行一次的短命令仍优先用 :!,这样上下文更简单。

应该选择哪一种方式

目的 推荐方式
临时回到启动 Vim 的 Shell Ctrl-Z,完成后 fg
执行一条短命令 :!command
连续执行多条 Shell 命令 :shell,完成后 exit
把命令输出插入文件 :read !command
把文本送给命令但不改原文 :write !command
用外部命令替换或格式化文本 :%!command 或可视选择后 !command
编译并在错误之间跳转 :make:copen
在 Vim 内运行持续交互程序 :terminal
关闭终端后仍要保留工作现场 tmuxscreen

Windows、PowerShell 与 WSL 的差异

Ctrl-Zjobsfgbg 这一套来自 Unix Shell 的作业控制。在 Linux、macOS、WSL 中的 Bash/Zsh 里通常可以直接使用;在原生 PowerShell 或 cmd.exe 中,不要假设它有完全相同的行为。

如果当前在 Windows 原生终端中使用 Vim,优先练习这些跨环境方式:

:!command
:shell
:read !command
:terminal

若希望完整练习 Ctrl-Z → jobs → fg,建议在 WSL 的 Bash/Zsh 终端中打开 Vim。

20 分钟针对性练习

不要一次背完所有命令,可以按下面顺序练习:

第一轮:只练挂起与恢复(5 分钟)

  1. 用 Vim 打开一个临时文件;
  2. 写入两行文字并 :w
  3. Ctrl-Z
  4. 执行 jobs -l
  5. 执行 fg 回到 Vim;
  6. 重复三次,直到形成肌肉记忆。

第二轮:在 Vim 内执行命令(5 分钟)

依次执行:

:!pwd
:!ls
:r !date

观察“只显示输出”和“把输出插入文件”的区别。

第三轮:让外部命令处理文本(5 分钟)

准备几行乱序并有重复的单词,执行:

:%!sort -u

然后按 u 撤销,再重复一次。

第四轮:建立自己的选择习惯(5 分钟)

遇到任务时先问:

  • 我只是执行一条命令吗?用 :!
  • 我要回到原 Shell 吗?用 Ctrl-Z,再用 fg
  • 我要把输出放进文章吗?用 :read !
  • 我要转换一段文本吗?用范围加 !
  • 这是持续运行的交互任务吗?用 :terminaltmux

最小速查表

Ctrl-Z          暂停 Vim,回到原 Shell(Unix/WSL)
jobs 查看 Shell 作业
fg 恢复最近的前台作业

:!pwd 执行一条外部命令
:sh 启动子 Shell,exit 返回 Vim
:r !date 插入外部命令输出
:w !wc -l 把缓冲区送给外部命令
:%!sort -u 用外部命令过滤整个缓冲区
:make 编译并填充 quickfix
:copen 打开 quickfix 列表
:terminal 打开内置终端

常见误区

  1. 把暂停当成后台运行Ctrl-Z 后 Vim 是停止状态,通常用 fg 恢复。
  2. bg 恢复交互式 Vim:它仍需要终端输入,常会再次停止。
  3. 认为暂停后可以安全关闭终端:暂停不提供持久会话保障。
  4. 忘记保存再运行程序:外部编译器读取的是磁盘文件,不一定能看到未保存的缓冲区内容。
  5. 混淆 :!cmd:%!cmd:前者只执行,后者会用命令输出替换整个缓冲区。
  6. 忽略路径中的空格:脚本化命令应配合 shellescape()expand('%:p')

Vim 内置帮助

遇到版本差异时,以当前 Vim 自带帮助为准:

:help suspend
:help :!
:help :shell
:help :read!
:help :write_c
:help filter
:help :make
:help quickfix
:help terminal

这部分学习完成后,下一步最值得继续的是 quickfix 工作流:把编译、测试和全文搜索的结果统一成一个可以跳转的错误列表。

3d打印 ai辅助设计 algorithm algorithms anymal apriltag ardupilot axis-angle bang-bang blender bode cadquery calibration camera calibration chrome cmake cmakelists cnn colcon conan control cpp cpu d435i dagger data_struct db design-pattern dots economics eigen factory-pattern fcpx fiducial marker figure finance forge fov freecad gazebo gdb git gnu hardware ibus imu interest isaac gym isaac lab isaaclab kdl latent variable latex launch learning-notes legged locomotion legged robotics legged-robot life linux linux-kernel mac math matlab matrix memory mlp money motion-control motor moveit mpc mujoco network ocs2 ode openscad operator optimal algorithm optimal-control perf performance personal-finance pixhawk pixhawk 6c policy distillation ppo privileged learning profiling px4 python qgroundcontrol qos quadrotor realsense reinforcement learning representation learning reward tuning rnn robot robotics ros ros2 rtb security shell sim-to-real simulation socket stairs stl stm32 tcp-ip teacher policy teacher-student temporal convolution thread tools twist ubuntu uml unitree urdf vae valgrind vcxsrv velocity vim web wifi wiring work wsl 中文输入 交叉编译 依赖管理 分支管理 四旋翼 四足机器人 实验诊断 强化学习 机器人 机器人控制 机器人视觉 构建系统 深度学习 深度相机 点云 版本控制 神经网络 自主回充 航模 视觉定位 训练曲线 输入法 配置类 采购记录 飞控
知识共享许可协议