协慌网

登录 贡献 社区

vim“用 sudo 写” 技巧如何工作?

很多人可能已经看到了允许你在需要 root 权限的文件上写的命令,即使你忘了用 sudo 打开 vim:

:w !sudo tee %

问题是我不知道这里到底发生了什么。

我已经想到了这个: 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

为了让所有人都有意义, %应输出文件名(作为tee的参数),但我找不到有关此行为的帮助的参考。

tl; dr有人可以帮我剖析这个命令吗?

答案

:w !sudo tee % ...

%表示 “当前文件”

正如eugene 指出的%确实意味着 “当前文件名”。 Vim 中的另一个用途是替换命令。例如, :%s/foo/bar表示 “ 在当前文件中 ,用bar替换foo出现次数”。如果在键入:s之前突出显示某些文本,您会看到突出显示的行代替%作为替换范围。

:w没有更新您的文件

这个技巧的一个令人困惑的部分是你可能会想:w正在修改你的文件,但事实并非如此。如果您打开并修改了file1.txt ,则运行:w file2.txt ,它将是 “另存为”; file1.txt不会被修改,但当前缓冲区内容将被发送到file2.txt

您可以替换 shell 命令来接收缓冲区内容 ,而不是file2.txt 。例如, :w !cat将只显示内容。

如果 Vim 没有运行 sudo 访问,它的:w不能修改受保护的文件,但是如果它将缓冲区内容传递给 shell,shell 中的命令可以用 sudo 运行 。在这种情况下,我们使用tee

了解发球台

至于tee ,在正常的 bash 管道情况下将tee命令描绘成 T 形管道:它将输出定向到指定的文件, 并将其发送到标准输出 ,可以通过下一个管道命令捕获。

例如,在ps -ax | tee processes.txt | grep 'foo' ,进程列表将被写入文本文件传递给grep

+-----------+    tee     +------------+
     |           |  --------  |            |
     | ps -ax    |  --------  | grep 'foo' |
     |           |     ||     |            |
     +-----------+     ||     +------------+
                       ||   
               +---------------+
               |               |
               | processes.txt |
               |               |
               +---------------+

(使用Asciiflow创建的图表 。)

有关详细信息,请参阅tee手册页

发球作为一个黑客

在你的问题所描述的情况下, 使用tee是一种黑客攻击,因为我们忽略了它的一半sudo tee写入我们的文件并将缓冲区内容发送到标准输出,但我们忽略标准输出 。在这种情况下,我们不需要将任何内容传递给另一个管道命令; 我们只是使用tee作为编写文件的替代方式,以便我们可以使用sudo调用它。

让这个技巧变得容易

你可以将它添加到.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部分显式抛弃标准输出,因为正如我所说,我们不需要将任何内容传递给另一个管道命令。

在执行的命令行中, %代表当前文件名 。这记录在:help cmdline-special

In Ex commands, at places where a file name can be used, the following
characters have a special meaning.
        %       Is replaced with the current file name.

正如您已经发现的那样:w !cmd将当前缓冲区的内容传递给另一个命令。 tee所做的是将标准输入复制到一个或多个文件,也复制到标准输出。因此, :w !sudo tee % > /dev/null 在 root 时有效地将当前缓冲区的内容写入当前文件。另一个可用于此的命令是dd

:w !sudo dd of=% > /dev/null

作为快捷方式,您可以将此映射添加到.vimrc

" Force saving files that require root permission 
cnoremap w!! w !sudo tee > /dev/null %

使用上面的内容,您可以键入:w!!<Enter>以 root 身份保存文件。

这也很有效:

:w !sudo sh -c "cat > %"

这是受 @Nathan Long 评论的启发。

通知

"必须使用'而不是'因为我们希望在传递给 shell 之前扩展%