题 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;博士 有人可以帮我剖析这个命令吗?


1152
2018-04-08 14:36


起源


@Nathan:会的 :w !sudo cat > % 不起作用,不污染标准输出? - Bjarke Freund-Hansen
@bjarkef - 不,那不行。在这种情况下, sudo 适用于 cat,但不是 >,所以不允许这样做。您可以尝试在sudo子shell中运行整个命令,例如 :w !sudo sh -c "cat % > yams.txt",但这也不会起作用,因为在子shell中, % 没有;你将删除文件的内容。 - Nathan Long
@NathanLong:你可以使用 :w !sudo sh -c 'cat $1 > yams.txt' - %,但我不知道如何处理特殊的字符或空格 - knittl
我只想在输入该命令后添加一条警告消息。如果是这样,请按L.然后,您将被要求按Enter键。你最终会保存你的文件吗? - pablofiumara
@NathanLong @knittl: :w !sudo sh -c "cat >%" 实际上效果一样好 sudo tee % 因为Vim替换了文件名 % 在它进入子壳之前。但是,如果文件名中包含空格,它们都不起作用;你必须做 :w !sudo sh -c "cat >'%'" 要么 :w !sudo tee "%" 解决这个问题。 - Han Seoul-Oh


答案:


:w !sudo tee %...

% 表示“当前文件”

eugene y指出% 确实意味着“当前的文件名”。 Vim中的另一个用途是替换命令。例如, :%s/foo/bar 意思是“在当前文件中,替换出现的 foo 同 bar“如果你在打字之前突出显示一些文字 :s,你会看到突出显示的线代替了 % 作为你的替代范围。

:w 没有更新您的文件

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

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

如果Vim没有运行sudo访问权限,那么 :w 无法修改受保护的文件,但如果它将缓冲区内容传递给shell, shell中的命令 能够 与sudo一起运行。在这种情况下,我们使用 tee

了解发球台

至于 tee,图片 tee 在正常的bash管道情况下命令为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部分 明确地 抛弃标准输出,因为,正如我所说,我们不需要将任何东西传递给另一个管道命令。


1276
2017-08-16 12:49



特别喜欢你的符号“w !!”使用“sudo !!”后很容易记住在命令行上。 - Aidan Kane
所以这用了 tee 因为它能够将stdin写入文件。我很惊讶没有一个程序可以做到这一点(我找到了一个我从未听说过的程序 sponge 这样做)。我想典型的“写一个文件流”是由内置的shell执行的。 Vim's !{cmd} 不分叉壳(分叉 cmd 代替)?也许更明显的一点是使用一些工作变体 sh -c ">" 而不是 tee。 - Steven Lu
@Steven Lu: sponge 是的一部分 moreutils 除了基于Debian的发行版之外,几乎每个发行版都有一个包。 moreutils 有一些非常好的工具,与更常见的工具,如 xargs 和 tee。 - Swiss
如何扩展此别名也告诉vim自动将更改的文件内容加载到当前缓冲区?它问我,如何实现自动化? - Zlatko
@ user247077:在这种情况下, cat 以root身份运行,输出由shell重定向,而shell不以root身份运行。它是一样的 echo hi > /read/only/file。 - WhyNotHugo


在执行的命令行中, % 代表着 当前文件名。这是记录在案 :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 有效地将当前缓冲区的内容写入当前文件 虽然是根。另一个可用于此的命令是 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身份保存文件。


88
2018-04-08 14:45



有趣, :help _% 提出你输入的内容,但是 :help % 调出括号匹配键。我不会想到尝试下划线前缀,是vim文档中的某种模式吗?在寻求帮助时还有其他“特殊”的事情要尝试吗? - David Pope
@David:The help 命令跳转到标签。你可以看到可用的标签 :h help-tags。您还可以使用命令行完成来查看匹配的标记: :h cmdline<Ctrl-D> (要么 :h cmdline<Tab> 如果你设置 wildmode 因此) - Eugene Yarmash
+1为 :h help-tags,可搜索和有用。谢谢! - David Pope
我不得不使用 cmap w!! w !sudo tee % > /dev/null 在我的.vimrc文件中使这个工作。是个 %在上面的答案中放错了地方? (这里没有vim专家。) - DMfll
@jazzpi:你错了。 shell实际上并不关心在命令行上执行文件重定向的位置。 - Eugene Yarmash


这也很有效:

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

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

注意

" 必须用来代替 ' 因为我们想要 % 在传递给shell之前要扩展。


18
2017-07-29 08:08



虽然这可能有效,但它也可以让sudo访问多个程序(sh和cat)。通过替换,其他示例可以更安全 tee 同 /usr/bin/tee 防止PATH修改攻击。 - idbrii


:w  - 写一个文件。

!sudo  - 调用shell sudo命令。

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

教程链接


16
2018-06-04 06:02





接受的答案涵盖了所有内容,因此我将再举一个例子 捷径 我使用的,作为记录。

把它添加到你的 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> 是怎么写的  符号在这里分开两个命令。

希望能帮助到你。另见其他问题:


3
2018-01-13 07:12





我想建议另一种方法 “Oups我忘了写 sudo 打开我的文件时“ 问题:

而不是接收 permission denied并且必须输入 :w!!,我发现有条件的更优雅 vim 这样的命令 sudo vim 如果文件所有者是 root

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

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

它的效果非常好。

这是一个更多 bash以中心的方式比 vim - 所以不是每个人都喜欢它。

当然:

  • 有些用例会失败(当文件所有者不是时) root 但需要 sudo,但无论如何都可以编辑功能)
  • 使用时没有意义 vim 只读文件(据我所知,我用 tail 要么 cat 对于小文件)

但我发现这会带来更好的效果 开发用户体验,这是恕我直言,使用时往往会被遗忘的东西 bash。 :-)


1
2018-02-28 18:21