你们中的许多人可能已经看到允许您在需要 root 权限的文件上写入的命令,即使您忘记使用 sudo 打开 vim:
:w !sudo tee %
问题是我不明白这里到底发生了什么。
我已经想通了: w就是为了这个
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} is executed like with ":!{cmd}", any '!' is replaced with the previous command |:!|.
所以它将所有行作为标准输入传递。
该部件以管理员权限!sudo tee调用。tee
!sudo tee
tee
为了让所有人都明白,%应该输出文件名(作为 的参数tee),但我找不到有关此行为的帮助的参考资料。
%
tl; dr有人可以帮我剖析这个命令吗?
在:w !sudo tee %…
正如eugene y 指出的那样,%确实意味着“当前文件名”,它被传递给tee它以便它知道要覆盖哪个文件。
(在替换命令中,它略有不同;如图:help :%所示,它是equal to 1,$ (the entire file)(感谢@Orafu 指出这不会评估文件名)。例如,:%s/foo/bar表示“在当前文件中,将出现的地方foo替换为bar。”如果您突出显示输入之前的一些文本:s,您会看到突出显示的行代替了%您的替换范围。)
:help :%
equal to 1,$ (the entire file)
:%s/foo/bar
foo
bar
:s
这个技巧的一个令人困惑的部分是您可能认为:w正在修改您的文件,但事实并非如此。如果你打开修改file1.txt,然后运行:w file2.txt,那就是“另存为”;file1.txt不会被修改,但当前缓冲区内容将被发送到file2.txt.
:w
file1.txt
:w file2.txt
file2.txt
取而代之的是file2.txt,您可以替换为 shell 命令来接收缓冲区内容。例如,:w !cat将只显示内容。
:w !cat
如果 Vim 没有使用 sudo 访问运行,:w它就不能修改受保护的文件,但是如果它将缓冲区内容传递给 shell,则可以*使用 sudo 运行*shell中的命令。在这种情况下,我们使用tee.
至于tee,将tee命令想象成正常 bash 管道情况下的 T 形管道:它将输出定向到指定的文件,并将其发送到标准输出,可以由下一个管道命令捕获。
例如,在 中ps -ax | tee processes.txt | grep 'foo',进程列表将被写入文本文件并传递给grep.
ps -ax | tee processes.txt | grep 'foo'
grep
+-----------+ tee +------------+ | | -------- | | | ps -ax | -------- | grep 'foo' | | | || | | +-----------+ || +------------+ || +---------------+ | | | processes.txt | | | +---------------+
(使用Asciiflow创建的图表。)
有关更多信息,请参见tee手册页。
在您的问题描述的情况下,使用tee是一种黑客行为,因为我们忽略了它的一半功能。sudo tee写入我们的文件并将缓冲区内容发送到标准输出,但我们忽略标准输出。在这种情况下,我们不需要将任何东西传递给另一个管道命令;我们只是将tee其用作编写文件的另一种方式,因此我们可以使用sudo.
sudo tee
sudo
您可以将其添加到您的.vimrc中,以使此技巧易于使用:只需键入:w!!.
.vimrc
:w!!
" Allow saving of files as sudo when I forgot to start vim using sudo. cmap w!! w !sudo tee > /dev/null %
该> /dev/null部分明确丢弃了标准输出,因为正如我所说,我们不需要将任何内容传递给另一个管道命令。
> /dev/null