在Linux环境下,如果直接使用VI/VIM命令编辑没有修改权限的文件时,保存的时候就会提示用户无法进行保存操作,一般的解决方法只能是关闭文件重新以sudo权限打开该文件编辑后再保存(前提是用户具有sudo权限)。其实,在VI/VIM模式下通过一些简单的命令,就能在不关闭当前文件的情况下达到保存文件的目的。
方法一
关于%! sudo tee % > /dev/null这条命令的说明如下
此命令是把当前文件(即%)作为stdin传给sudo tee命令来执行。
方法二
在Linux上工作的朋友很可能遇到过这样一种情况,当你用Vim编辑完一个文件时,运行:wq保存退出,突然蹦出一个错误:
E45: 'readonly' option is set (add ! to override)
这表明文件是只读的,按照提示,加上!强制保存::w!,结果又一个错误出现:
"readonly-file-name" E212: Can't open file for writing
文件明明存在,为何提示无法打开?这错误又代表什么呢?查看文档:help E212:
For some reason the file you are writing to cannot be created or overwritten.
The reason could be that you do not have permission to write in the directory
or the file name is not valid.
原来是可能没有权限造成的。此时你才想起,这个文件需要root权限才能编辑,而当前登陆的只是普通用户,在编辑之前你忘了使用sudo来启动Vim,所以才保存失败。于是为了防止修改丢失,你只好先把它保存为另外一个临时文件temp-file-name,然后退出Vim,再运行sudo mv temp-file-name readonly-file-name覆盖原文件。
但这样操作过于繁琐。而且如果只是想暂存此文件,还需要接着修改,则希望保留Vim的工作状态,比如编辑历史,buffer状态等等,该怎么办?能不能在不退出Vim的情况下获得root权限来保存这个文件?
解决方案
答案是可以,执行这样一条命令即可:
:w !sudo tee %
接下来我们来分析这个命令为什么可以工作。首先查看文档:help :w,向下滚动一点可以看到:
*:w_c* *:write_c*:[range]w[rite] [++opt] !{cmd}Execute {cmd} with [range] lines as standard input(note the space in front of the '!'). {cmd} isexecuted like with ":!{cmd}", any '!' is replaced withthe previous command |:!|.The default [range] for the ":w" command is the whole buffer (1,$)
把这个使用方法对应前面的命令,如下所示:
: w !sudo tee %| | | |:[range]w[rite] [++opt] !{cmd}
我们并未指定range
,参见帮助文档最下面一行,当range
未指定时,默认情况下是整个文件。此外,这里也没有指定opt。
Vim中执行外部命令
接下来是一个叹号!,它表示其后面部分是外部命令,即sudo tee %
。文档中说的很清楚,这和直接执行:!{cmd}
是一样的效果。后者的作用是打开shell执行一个命令,比如,运行:!ls
,会显示当前工作目录下的所有文件,这非常有用,任何可以在shell中执行的命令都可以在不退出Vim的情况下运行,并且可以将结果读入到Vim中来。试想,如果你要在Vim中插入当前工作路径或者当前工作路径下的所有文件名,你可以运行:
:r !pwd或:r !ls
此时所有的内容便被读入至Vim,而不需要退出Vim,执行命令,然后拷贝粘贴至Vim中。有了它,Vim可以自由的操作shell而无需退出。
命令的另一种表示形式
再看前面的文档:
Execute {cmd} with [range] lines as standard input
所以实际上这个:w并未真的保存当前文件,就像执行:w new-file-name时,它将当前文件的内容保存到另外一个new-file-name的文件中,在这里它相当于一个另存为,而不是保存。它将当前文档的内容写到后面cmd的标准输入中,再来执行cmd,所以整个命令可以转换为一个具有相同功能的普通shell命令:
$ cat readonly-file-name | sudo tee %
这样看起来”正常”些了。其中sudo很好理解,意为切换至root执行后面的命令,tee和%是什么呢?
%的意义
我们先来看%,执行:help cmdline-special
可以看到:
In Ex commands, at places where a file name can be used, the following
characters have a special meaning. These can also be used in the expression
function expand() |expand()|.
% Is replaced with the current file name. *:_%* *c_%*
在执行外部命令时,%会扩展成当前文件名,所