我们经常看到编辑后试图在 vim 中保存的文件被报告为只读。解决这个问题的方法是添加!wq
,我试图弄清楚内部是如何允许vim程序获得足够的权限来写入只读文件的?
是否有一个内部标志被切换或者vim暂时获得了一段时间的特权?
答案1
当您w!
在 Vim 中执行此操作时,实际发生的情况取决于文件的所有者。
如果你(当前用户)是文件的所有者,Vim 会在重写文件之前将权限更改为可写。然后,它会删除写入权限,将权限位恢复到一开始的状态。
如果你不是文件的所有者,但如果你在当前目录中有写权限,Vim 会删除原始文件并将文档写入同名的新文件中。然后,新文件将被分配与原始文件相同的权限,但将归您所有。
Vim 在任何时候都不会获得能够写入文件的提升权限。
上述机制是任何需要写入只读文件的程序都必须选择的可用选项(即,在写入文件时临时更改权限,或删除文件并创建新文件),并且Vim 最终选择做什么可能最终取决于许多可配置的设置。
从下面的评论中可以看出,上述内容存在一些混乱。如果您想亲眼看看在您的特定 Unix 品牌上安装 Vim 时实际发生了什么,我建议您跟踪 Vim 在写入只读文件时执行的系统调用。如何完成此操作取决于您使用的 Unix。在 Linux 上,这可能是通过例如strace vim file
(然后编辑文件,保存并w!
退出)来完成的。
这是第一种情况( OpenBSD 上ktrace
+的输出kdump
):
13228 vim CALL chmod(0x19b1d94b4b10,0100644<S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH|S_IFREG>)
13228 vim NAMI "file"
13228 vim RET chmod 0
13228 vim CALL lseek(3,0x1000,SEEK_SET)
13228 vim RET lseek 4096/0x1000
13228 vim CALL write(3,0x19b1e0aa9000,0x1000)
这会更改文件的权限,使其可写(S_IWUSR
与 一起使用的标志chmod()
)并将缓冲区写入其中。
然后它设置原始权限:
13228 vim CALL fchmod(4,0100444<S_IRUSR|S_IRGRP|S_IROTH|S_IFREG>)
13228 vim RET fchmod 0
13228 vim CALL close(4)
13228 vim RET close 0
对于另一种情况:
它首先取消链接(删除)文件,然后重新创建它(在写入文件并稍后更改权限之前):
44487 vim CALL unlink(0x79fdbc1f000)
44487 vim NAMI "file"
44487 vim RET unlink 0
44487 vim CALL open(0x79fdbc1f000,0x201<O_WRONLY|O_CREAT>,0644<S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH>)
44487 vim NAMI "file"
44487 vim RET open 4
答案2
Vim 无法获得额外的权限。只是:w!
覆盖了内部'readonly'
选项,该选项的设置可能是因为:
-R
您已通过命令行选项打开该文件或:view
使用:edit
, 或:setlocal readonly
- Vim 识别出该文件当前没有写权限
对于后一种情况,文件写入可能仍然是可能的,因为 Vim (默认情况下)创建一个新文件然后用它替换原始文件。这仍然取决于以允许这样做的方式设置的权限。
要真正获得打开 Vim 的用户没有写入权限的权限,:w !sudo tee >/dev/null file
必须直接使用该技巧,或者通过类似的插件Sudo编辑。
答案3
您永远无法写入没有写入权限的文件。但是,如果您对该目录具有写入权限,则可以删除该文件。
VIM 使用的技巧是删除文件并写入新文件。
可以通过检查来表明这是 VIM 使用的方法,而无需阅读源代码索引节点号之前和之后:
$ touch foo
$ chmod u-w foo
$ ls -li foo
60818465 -r--r----- 1 philip philip 0 Feb 25 10:24 foo
$ vi foo
$ # edit the file and save with :w!
$ ls -li foo
60818467 -r--r----- 1 philip philip 8 Feb 25 10:25 foo
请注意,索引节点号已更改,表明新文件与您编辑的文件不同。
仅供参考我当前的配置非常短:
runtime! debian.vim
if has("syntax")
syntax on
endif
set tabstop=4
set autoindent
安装的 debian 软件包有:
vim 2:8.1.0875-1
vim-common 2:8.1.0875-1
vim-runtime 2:8.1.0875-1
vim-tiny 2:8.1.0875-1
答案4
当 vim 处于 vi 兼容模式时,:w!只会覆盖缓冲区的只读模式,但不会尝试来回更改文件权限,通过将不同文件重命名为原始名称来绕过权限,或者做其他业余时间的事情。
恕我直言,这是唯一正确的行为——控制它的标志W
来自cpoptions
/ cpo
。从:help cpo
:
*'cpoptions'* *'cpo'* *cpo* 'cpoptions' 'cpo' string (Vim default: "aABceFs", Vi default: all flags) ... *cpo-W* W Don't overwrite a readonly file. When omitted, ":w!" overwrites a readonly file, if possible.
:set compatible
将打开所有cpo
标志。您可以W
使用:set cpo+=W
或只更改标志set cpo-=W
。