Vim 交换文件处理、内容恢复、保存退出与权限提升
当 Vim 编辑器因异常退出(断电、终端中断、强制关闭)残留交换文件时,再次打开同一文件会触发"交换文件已存在"提示。可根据恢复需求选择删除交换文件或使用 vim -r 恢复内容。同时区分 Vim 内部只读标记与系统文件权限限制,掌握常规保存退出、特权文件提权编辑方案。
一、交换文件处理
1. 前置检查
处理交换文件前,先确认交换文件存在性,以及是否仍有活跃 Vim 进程占用文件。
# 查找交换文件,后缀通常为 .swp / .swo / .swn
ls -a /etc/default/ | grep grub
# 排查残留 vim 进程
ps aux | grep vim
进程不存在时,交换文件属于异常退出残留,可以进行后续处理。
2. 交换文件清理方式
-
Vim 弹窗界面:提示页直接按
D,删除交换文件并打开文件,优先选用。 -
rm:常规删除,支持通配批量清理sudo rm /etc/default/.grub.swp sudo rm /etc/default/.grub.sw* -
unlink:仅支持单个文件删除sudo unlink /etc/default/.grub.swp -
find:路径不确定时检索并删除sudo find /etc/default -name ".grub.swp" -delete -
shred:覆写后删除,适合敏感文件;SSD、日志文件系统上擦除效果有限,普通交换文件无需使用sudo shred -u /etc/default/.grub.swp -
mv:不直接删除,转移至临时目录留存观察sudo mv /etc/default/.grub.swp /tmp/
3. 交换文件内容恢复 vim -r
适合异常退出、缓冲区内容未落盘的场景。
-
恢复读取
sudo vim -r /etc/default/grub成功加载交换文件时输出:
Using swap file ".grub.swp" Original file "/etc/default/grub" Recovery completed. -
校验比对:恢复后先另存副本,再使用
diff比对差异:w /etc/default/grub.recovered diff /etc/default/grub /etc/default/grub.recovered -
确认无误后保存退出
:wq -
收尾:恢复完成必须清理交换文件,避免下次打开重复弹窗
sudo rm /etc/default/.grub.sw*
约束说明:交换文件删除后无法执行恢复;Vim 周期性写入交换文件,异常退出瞬间未同步的改动会丢失;引导类配置(
/etc/default/grub)修改或恢复后,校验语法并执行更新:sudo update-grub或sudo grub2-mkconfig -o /boot/grub2/grub.cfg。
二、基础保存与退出命令
按下 ESC 进入命令模式执行。:w 等价 :write;:wq 等价 :write quit。
| 命令 | 说明 |
|---|---|
:w |
保存,不退出 |
:w! |
强制保存,仅绕过 Vim 内部只读标记,无法突破系统权限 |
:w filename |
另存为目标文件,不退出 |
:w! filename |
强制覆盖另存 |
:wq |
保存并退出,无论内容是否改动,都会更新文件 mtime |
:wq! |
强制保存退出,仅作用于 Vim 只读标记 |
:wq! filename |
强制另存后退出 |
:x |
保存退出;仅缓冲区改动时写入磁盘 |
:q |
不保存退出,无修改才可执行 |
:q! |
放弃修改,强制退出 |
:e! |
丢弃缓冲区改动,重读磁盘文件 |
ZZ |
普通模式,保存退出,行为接近 :wq |
ZQ |
普通模式,放弃修改退出,行为接近 :q! |
三、特权只读文件提权编辑方案
报错标识:
E45: 'readonly' option is set (add ! to override)
E212: Can't open file for writing
区分边界:
:w!/:wq!只能清除 Vim 内部只读标记;系统层面无写权限时,必须使用下面方案。
方案 A:原生内置 :w !sudo tee %(无插件,适合编辑中途发现权限不足)
基础命令:
:w !sudo tee %
推荐版本,屏蔽 tee 的标准输出:
:w !sudo tee % > /dev/null
语法拆解
| 组成 | 含义 |
|---|---|
:w !{cmd} |
Vim 语法,缓冲区内容作为 stdin 传给外部 shell 命令;与 :w! file 完全不同 |
sudo |
提升后续进程身份为 root |
tee |
独立二进制程序,读取标准输入写入文件;由 sudo 拉起后以 root 打开目标文件 |
% |
Vim 内置宏,展开当前文件完整路径 |
数据流:
缓冲区内容 | sudo tee 当前文件路径
为什么
sudo > file无效 ?
>、>>属于 shell 重定向,打开文件操作由当前 shell 用户执行,不会被 sudo 提权。
写入后提示与操作
写入成功触发告警:
W12: Warning: File "xxx" has changed and the buffer was changed in Vim as well
[O]K, (L)oad File:
L:从磁盘重载文件,缓冲区和磁盘保持一致O:保留当前缓冲区内容
推荐后续执行,同步缓冲区状态:
:e!
限制与扩展
-
:wq !sudo tee %语法非法,:wq不支持管道外部命令 -
当前用户必须具备 sudo 权限,
tee在系统PATH -
直接覆盖原文件,写入前确认内容
-
另存到其他路径,替换
%::w !sudo tee /etc/nginx/nginx.conf > /dev/null
Vimrc 配套快捷配置(免插件)
" 定义 :W,提权写入并自动重载磁盘
command! W execute 'w !sudo tee % > /dev/null' | edit!
" :w!! 缩写,不会误匹配类似 w!!abc 文本
cnoreabbrev <expr> w!!
\ getcmdtype() == ':' && getcmdline() == 'w!!' ? 'W' : 'w!!'
" 打开文件自动权限检测,不可写时提示
augroup SudoEditCheck
autocmd!
autocmd BufReadPost * call s:CheckFilePermission()
augroup END
function! s:CheckFilePermission() abort
if bufname('%') ==# ''
\ || &buftype !=# ''
\ || !filereadable(expand('%:p'))
return
endif
let l:file = expand('%:p')
if !filewritable(l:file)
echohl WarningMsg
echo '[sudo-edit] 当前文件不可写,保存时可使用 :W 或 :w!!'
echohl None
endif
endfunction
检测逻辑:跳过无名缓冲区、帮助页、quickfix、终端缓冲区;仅判断系统写权限,不检测 Vim 内部 readonly;该映射和 suda.vim 可以共存。
方案 B:插件方案,适合高频编辑系统配置
suda.vim(推荐,Vim / Neovim)
仓库:lambdalisue/suda.vim
call plug#begin('~/.vim/plugged')
Plug 'lambdalisue/suda.vim'
call plug#end()
let g:suda_smart_edit = 1
let g:suda#prompt = 'Password: '
cabbrev sw SudaWrite
cabbrev sr SudaRead
nnoremap <silent> <leader>w :SudaWrite<CR>
执行 :PlugInstall 安装。
常用命令:
:SudaRead
:SudaRead /etc/hosts
:SudaWrite
:SudaWrite /etc/profile
:e suda:///etc/hosts
开启 g:suda_smart_edit = 1,自动识别权限,按需提权读写。
sudo.vim(老旧备选)
仓库:vim-scripts/sudo.vim,功能精简,仅遗留环境选用。
call plug#begin('~/.vim/plugged')
Plug 'vim-scripts/sudo.vim'
call plug#end()
cabbrev sw SudoWrite
cabbrev sr SudoRead
nnoremap <silent> <leader>w :SudoWrite<CR>
方案 C:sudoedit / sudo -e(优先首选,提前预知需要特权编辑)
安全模型优于 sudo vim,编辑器进程不以 root 身份运行。
sudoedit /etc/hosts
sudo -e /etc/hosts
运行流程:
- 在
/tmp创建副本,副本归属当前普通用户 - 使用当前用户身份拉起编辑器,加载用户自身 vim 配置、插件
- 退出编辑器,副本改动存在时,root 身份写回原文件
- 自动删除临时文件(前缀
.sudoedit_) - 保留原文件权限、属主、属组不变
编辑器环境变量优先级:$SUDO_EDITOR > $VISUAL > $EDITOR,未配置默认回退 vi。
# 写入 ~/.bashrc / ~/.zshrc
export EDITOR=vim
# 单独指定 sudoedit 使用编辑器(优先级更高)
export SUDO_EDITOR=vim
export EDITOR=nano
校验:
sudoedit --version
echo $SUDO_EDITOR
echo $EDITOR
sudoedit 和 sudo vim 对比
| 维度 | sudoedit | sudo vim |
|---|---|---|
| 进程身份 | 当前普通用户 | root |
| 加载配置 | 用户的 vimrc | root 的 vimrc |
| 安全边界 | 编辑器漏洞无法直接获取 root | 编辑器内可执行 root 命令 |
| 交换文件 | 当前用户可写目录 | 可能落在受保护目录 |
| 适用场景 | 日常修改系统配置 | 需要 root 上下文执行操作 |
sudoers 免密配置(生产环境谨慎)
修改 sudoers 必须使用 visudo,防止语法错误锁死 sudo:
sudo visudo
示例条目:
# 单用户
username ALL=(ALL) NOPASSWD: /usr/bin/sudoedit, /usr/bin/vim
# 用户组
%admin ALL=(ALL) NOPASSWD: /usr/bin/sudoedit
# 最小权限,推荐写法,限定文件路径
username ALL=(ALL) NOPASSWD: /usr/bin/sudoedit /etc/hosts, /usr/bin/sudoedit /etc/nginx/*
全局 sudo 免密(不推荐生产)
username ALL=(ALL:ALL) NOPASSWD: ALL
提权方案对比
| 方式 | 特点 | 适用场景 |
|---|---|---|
:w !sudo tee % |
零插件,随用随执行 | 已经打开文件编辑,中途发现无权保存 |
| suda.vim | 自动权限检测、suda:// 协议、读写命令分离 | 频繁修改系统配置,固定工作流 |
| sudo.vim | 老式插件,功能简单 | 旧环境、历史存量配置 |
| sudoedit | 安全最优,编辑器非 root 运行,保留文件属性 | 提前知道要编辑特权文件 |
四、注意事项与约束说明
- 交换文件恢复相关:交换文件删除后,
vim -r无法恢复未保存内容;Vim 仅周期性落盘至交换文件,异常退出瞬间未同步的改动会丢失;恢复时若磁盘原文件已变更,出现版本冲突需要逐段比对内容,确认差异后再覆盖写入。 - 系统特殊文件:
/etc/default/grub这类引导配置,修改或恢复完成后,需要校验语法,再执行sudo update-grub或sudo grub2-mkconfig -o /boot/grub2/grub.cfg生效。 - 删除工具边界:
rmdir仅支持删除空目录,不能删除普通文件;shred的覆写擦除在 SSD、日志文件系统上效果有限,对于.swp临时交换文件一般无需使用。 - 保存命令权限边界:
:w!、:wq!仅绕过 Vim 内部只读标记,无法突破操作系统层面的文件写权限限制 ;缺少系统写权限时,应选用:w !sudo tee %、sudoedit,或以sudo重新打开文件。 - 提权前置条件:所有依赖 sudo 的编辑、保存方案,都要求当前用户在 sudoers 列表内;无 sudo 权限时无法执行。
五、常见问题
Q:sudoedit 打开后是 vi 而非 vim?
A:设置 $SUDO_EDITOR=vim 或 $EDITOR=vim。
Q:提示 sudoedit: no editor found?
A:检查环境变量是否正确设置,或确认 /usr/bin/vim 路径存在。
Q:保存后原文件权限/所有者是否保持不变?
A:是。sudoedit 以 root 权限写回原文件,不改变原文件的权限位和所有者。
Q:临时副本存放在何处?
A:默认在 /tmp 下,文件名以 .sudoedit_ 为前缀。编辑结束后自动删除。
参考文献
- Vim 优雅的强制写入只读文件
- vim 普通用户保存,这条指令怎么理解呢
- vim 用 :w !sudo tee % 进行保存的终极奥义
- vim 中使用 sudo 保存文件
- linux - 如何在 vim 保存时获得 sudo 权限
- vim 如何获得 su 权限?
- Technical Analysis of the Vim 'Write with Sudo' Trick: Implementation and Mechanisms
- How To Save A File In Vim Without Restarting With Sudo
- suda.vim 仓库
- sudo.vim 仓库
sudoman page ---sudoedit/-e选项说明sudoersman page ---NOPASSWD标签用法