首页 文章

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

提问于
浏览
1205

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

6 回答

  • 3

    :w !sudo tee % ......

    %表示“当前文件”

    eugene y pointed out% 的确意味着"the current file name" . Vim中的另一个用途是替换命令 . 例如, :%s/foo/bar 表示“ in the current file ,用 bar 替换 foo 的出现次数” . 如果在键入 :s 之前突出显示某些文本,您会看到突出显示的行代替 % 作为替换范围 .

    :w未更新您的文件

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

    而不是 file2.txt ,你可以 substitute a shell command to receive the buffer contents . 例如, :w !cat 将只显示内容 .

    如果Vim没有使用sudo访问运行,则 :w 无法修改受保护的文件,但如果它将缓冲区内容传递给shell,则为 a command in the shell can be run with sudo . 在这种情况下,我们使用 tee .

    了解发球台

    至于 tee ,在正常的bash管道情况下将 tee 命令描述为T形管道:它将输出定向到指定的文件和 also sends it to standard output ,可以通过下一个管道命令捕获 .

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

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

    (使用Asciiflow创建的图表 . )

    有关详细信息,请参阅tee man page .

    Tee as a hack

    在您的问题描述的情况下, using tee is a hack because we're ignoring half of what it does . sudo tee 写入我们的文件,并将缓冲区内容发送到标准输出,但 we ignore standard output . 我们只是使用 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 部分 explicitly 会抛弃标准输出,因为正如我所说,我们不需要将任何内容传递给另一个管道命令 .

  • 91

    在执行的命令行中, % 代表 current file name . 这在: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 有效地将当前缓冲区的内容写入当前文件 while being 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身份保存文件 .

  • 16

    这也很有效:

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

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

    NOTICE

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

  • 1355

    :w - 写一个文件 .

    !sudo - 调用shell sudo命令 .

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

    tutorial link

  • 5

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

    将其添加到 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> 是如何编写管道符号以在此处分隔两个命令 .

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

  • 18

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

    而不是收到 permission denied ,并且必须输入 :w!! ,如果文件所有者是 root ,我发现如果有一个条件 vim 命令可以执行 sudo vim 更为优雅 .

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

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

    它的效果非常好 .

    这是一个比 vim 更为中心的方法,因此不是每个人都喜欢它 .

    当然:

    • 有些用例会失败(当文件所有者不是 root 但需要 sudo 时,但无论如何都可以编辑该功能)

    • 使用 vim 只读文件时没有意义(就我而言,我使用 tailcat 作为小文件)

    但是我发现这会带来更好的 dev user experience ,这是恕我直言,当使用 bash 时往往会被遗忘 . :-)

相关问题