vim 如何覆盖只读模式?

vim 如何覆盖只读模式?

我们经常看到编辑后试图在 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

相关内容