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


1410

许多人可能已经看到了允许您在需要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有人可以帮我剖析此命令吗?


5
@Nathan::w !sudo cat > %不能很好地工作,并且不会污染标准输出吗?
Bjarke Freund-Hansen

51
@bjarkef-不,这不起作用。在这种情况下,sudo应用于cat,但不适用于>,因此不允许使用。您可以尝试在sudo子外壳程序(如)中运行整个命令:w !sudo sh -c "cat % > yams.txt",但这也不起作用,因为在子外壳程序中%为nil;您将清空文件的内容。
内森·朗

3
我只想补充一点,在键入该命令后,可能会出现一条警告消息。如果是这样,请按L。然后,将要求您按Enter。这样做,您最终将保存文件。
pablofiumara

9
@NathanLong @knittl::w !sudo sh -c "cat >%"实际上工作得一样好,sudo tee %因为Vim %在到达子shell之前会用文件名代替。但是,如果文件名中包含空格,则它们都不起作用。您必须执行:w !sudo sh -c "cat >'%'":w !sudo tee "%"解决该问题。
韩汉城

1
使用:W保存并重新加载文件:命令W:execute':silent w!sudo tee%> / dev / null'| :编辑!
迭戈·罗伯托·多斯·桑托斯

Answers:


1610

:w !sudo tee %...

% 表示“当前文件”

正如eugene y所指出的%确实的意思是“当前文件名”,将其传递给tee它,以便它知道要覆盖哪个文件。

(在替换命令,这是略有不同;如:help :%所示,它是equal to 1,$ (the entire file)(感谢@Orafu的指出,这并不评估为文件名)。例如,。:%s/foo/bar意思是“ 在当前文件,替换的出现foobar”如果你亮点在输入一些文本之前:s,您会看到突出显示的行取代了%您的替换范围。)

:w 没有更新您的文件

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

相反file2.txt,您可以替代shell命令来接收缓冲区内容。例如,:w !cat将仅显示内容。

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

理解三通

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

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

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

(使用Asciiflow创建的图。)

有关更多信息,请参见tee手册页

发球台

在您的问题描述的情况下,使用teehack是因为我们忽略了它所做的一半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部分显式丢弃了标准输出,因为正如我所说,因为我们不需要将任何内容传递给另一个管道命令。


147
尤其像您的记号“ w !!” 使用“ sudo !!”后很容易记住 在命令行上。
艾丹·凯恩

11
因此,这利用tee了将stdin写入文件的能力。令我惊讶的是,没有一个程序可以做到这一点(我发现一个我从未听说过的程序sponge可以做到这一点)。我猜典型的“将流写入文件”是由内置的Shell执行的。Vim是否!{cmd}不分叉外壳(cmd而是分叉)?也许更明显的事情是使用sh -c ">"而不是的某些工作变体tee
史蒂文·卢

2
@Steven卢:sponge是的一部分moreutils包装上除了基于Debian的发行版几乎每一个分布。moreutils有一些非常漂亮的工具与诸如xargs和等更通用的工具相提并论tee
瑞士人

10
如何扩展这个别名以告诉vim自动将更改的文件内容加载到当前缓冲区?它要求我,如何使其自动化?
Zlatko 2013年

10
@ user247077:在这种情况下,cat将以root身份运行,并且输出将由外壳重定向,而不是以root身份运行。与相同echo hi > /read/only/file
WhyNotHugo 2015年

99

在执行的命令行中,%代表当前文件名。记录在: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做的是标准输入复制到一个或多个文件,也到标准输出。因此,在成为root的同时:w !sudo tee % > /dev/null有效地将当前缓冲区的内容写入当前文件。另一个可用于此的命令是: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。


1
有趣,:help _%显示您输入的内容,但:help %显示大括号匹配键。我不会想尝试使用下划线前缀,这是vim文档中的某种模式吗?在寻求帮助时,还有其他“特殊”的事情可以尝试吗?
David Pope'3

2
@David:help命令跳转到标签。您可以使用查看可用标签:h help-tags。您还可以使用命令行:h cmdline<Ctrl-D>:h cmdline<Tab>wildmode
补全功能

1
我必须cmap w!! w !sudo tee % > /dev/null在我的.vimrc文件中使用此功能。在%上面的答案放错了地方?(此处没有vim专家。)
dmmfll 2015年

@DMfll是的。答案中的命令将导致sudo tee > /dev/null /path/to/current/file实际上没有任何意义。(将对其进行编辑)
jazzpi 2015年

1
@jazzpi:你错了。Shell实际上并不关心在命令行上的何处进行文件重定向。
Eugene Yarmash 2015年

19

这也很好:

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

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

注意

"必须使用,而不是'因为我们要%在传递给shell之前进行扩展。


11
尽管这可能有效,但它也使sudo可以访问多个程序(sh和cat)。其他的例子可以通过更换更安全的tee使用/usr/bin/tee,以防止路径修改攻击。
idbrii 2014年

18

:w -写一个文件。

!sudo -调用shell sudo命令。

tee-使用tee重定向了write(vim:w)命令的输出。%只是当前文件名,即/etc/apache2/conf.d/mediawiki.conf。换句话说,tee命令以root身份运行,它接受标准输入并将其写入由%表示的文件中。但是,这将提示您重新加载文件(按L加载vim本身的更改):

教程链接


9

可接受的答案涵盖了所有内容,因此,我仅举一个记录所使用的快捷方式的示例。

将其添加到您的etc/vim/vimrc(或~/.vimrc)中:

  • cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!

哪里:

  • cnoremap:告诉vim在命令行中要关联以下快捷方式。
  • w!!:快捷方式本身。
  • execute '...':执行以下字符串的命令。
  • silent!:静默运行
  • write !sudo tee % >/dev/null:OP问题,添加了消息重定向NULL以发出干净命令
  • <bar> edit!:这个技巧是小菜一碟:它还调用edit命令以重新加载缓冲区,然后避免诸如缓冲区已更改的消息。<bar>这里是如何编写管道符号以分隔两个命令的方法。

希望能帮助到你。另请参阅其他问题:


无声!禁用了密码提示,因此您看不到它
Andy Ray

7

我想提出另一种方法来解决sudo打开文件时忘记写的操作”问题:

我发现有一个条件命令(如果文件所有者为的话)可以代替它permission denied,而不是必须输入。:w!!vimsudo vimroot

这很容易实现(甚至可能会有更优雅的实现,我显然不是bash专家):

function vim(){
  OWNER=$(stat -c '%U' $1)
  if [[ "$OWNER" == "root" ]]; then
    sudo /usr/bin/vim $*;
  else
    /usr/bin/vim $*;
  fi
}

而且效果很好。

这是一种bashvim- 更加集中的方法,因此并非每个人都喜欢它。

当然:

  • 在某些情况下它会失败(当文件所有者不是root但需要时sudo,但该功能仍可以编辑)
  • vim用于仅读取文件时没有任何意义(就我而言,我使用tailcat小文件)

但是我发现这带来了更好的开发用户体验,而IMHO在使用时往往会被遗忘bash。:-)


5
只是要知道,这要少得多。就我个人而言,我的大多数错误都是愚蠢的错误。因此,当我做一些可能既愚蠢又后果性的事情时,我宁愿抬头。这当然是一个优先事项,但是特权升级被认为是一种出于良心的行为。另外:如果您经常遇到这种情况,以至于使“:w !!” 足以自动静默sudo的麻烦(但仅在owner = root时);您可能需要检查当前的工作流程。
Payne

有趣的评论,尽管应该引起注意,因为在root查询密码时打开文件时。
奥古斯丁·里丁格

啊! 有区别。这取决于sudo用户是否设置了“ NOPASSWD”。
Payne '18

那么拥有NOPASSWD 没有那么宽容... :)
奥古斯丁·里丁格18/12/14 '17

4

对于新

由于互动通话的问题(https://github.com/neovim/neovim/issues/1716),我根据Beco博士的回答将其用于neovim:

cnoremap w!! execute 'silent! write !SUDO_ASKPASS=`which ssh-askpass` sudo tee % >/dev/null' <bar> edit!

这将打开一个对话框,ssh-askpass要求您输入sudo密码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.