简体   繁体   English

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

[英]How does the vim “write with sudo” trick work?

Many of you have probably seen the command that allows you to write on a file that needs root permission, even when you forgot to open vim with sudo:你们中的许多人可能已经看到允许您在需要 root 权限的文件上写入的命令,即使您忘记使用 sudo 打开 vim:

:w !sudo tee %

The thing is that I don't get what is exactly happening here.问题是我不明白这里到底发生了什么。

I have already figured this: w is for this我已经想到了这一点: 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 |:!|.

so it passes all the lines as standard input.所以它将所有行作为标准输入传递。

The !sudo tee part calls tee with administrator privileges. !sudo tee部分以管理员权限调用tee

For all to make sense, the % should output the filename (as a parameter for tee ), but I can't find references on the help for this behavior.为了让一切有意义, %应该输出文件名(作为tee的参数),但我找不到有关此行为的帮助的参考。

tl;dr Could someone help me dissect this command? tl;dr有人可以帮我剖析这个命令吗?

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

% means "the current file" %表示“当前文件”

As eugene y pointed out , % does indeed mean "the current file name", which is passed to tee so that it knows which file to overwrite.正如eugene y 指出的那样%确实表示“当前文件名”,它被传递给tee以便它知道要覆盖哪个文件。

(In substitution commands, it's slightly different; as :help :% shows, it's equal to 1,$ (the entire file) (thanks to @Orafu for pointing out that this does not evaluate to the filename). For example, :%s/foo/bar means " in the current file , replace occurrences of foo with bar ." If you highlight some text before typing :s , you'll see that the highlighted lines take the place of % as your substitution range.) (在替换命令中,它略有不同;如:help :%所示,它equal to 1,$ (the entire file) (感谢 @Orafu 指出这不会计算文件名)。例如, :%s/foo/bar表示“在当前文件中,用bar替换foo出现。”如果在键入:s之前突出显示某些文本,您将看到突出显示的行代替%作为您的替换范围。)

:w isn't updating your file :w没有更新你的文件

One confusing part of this trick is that you might think :w is modifying your file, but it isn't.这个技巧的一个令人困惑的部分是您可能认为:w正在修改您的文件,但事实并非如此。 If you opened and modified file1.txt , then ran :w file2.txt , it would be a "save as";如果您打开并修改file1.txt ,然后运行:w file2.txt ,它将是“另存为”; file1.txt wouldn't be modified, but the current buffer contents would be sent to file2.txt . file1.txt不会被修改,但当前缓冲区内容将被发送到file2.txt

Instead of file2.txt , you can substitute a shell command to receive the buffer contents .代替file2.txt ,您可以替换一个 shell 命令来接收缓冲区内容 For instance, :w !cat will just display the contents.例如, :w !cat只会显示内容。

If Vim wasn't run with sudo access, its :w can't modify a protected file, but if it passes the buffer contents to the shell, a command in the shell can be run with sudo .如果 Vim 没有使用 sudo 访问权限运行,它的:w不能修改受保护的文件,但如果它将缓冲区内容传递给 shell,可以使用 sudo 运行 shell中的命令 In this case, we use tee .在这种情况下,我们使用tee

Understanding tee了解三通

As for tee , picture the tee command as a T-shaped pipe in a normal bash piping situation: it directs output to specified file(s) and also sends it to standard output , which can be captured by the next piped command.至于tee ,将tee命令想象成正常 bash 管道情况下的 T 形管道:它将输出定向到指定的文件,并将其发送到标准输出,它可以被下一个管道命令捕获。

For example, in ps -ax | tee processes.txt | grep 'foo'例如,在ps -ax | tee processes.txt | grep 'foo' ps -ax | tee processes.txt | grep 'foo' ps -ax | tee processes.txt | grep 'foo' , the list of processes will be written to a text file and passed along to grep . ps -ax | tee processes.txt | grep 'foo' ,进程列表将被写入一个文本文件传递给grep

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

(Diagram created with Asciiflow .) (使用Asciiflow创建的图表。)

See the tee man page for more info.有关更多信息,请参阅tee手册页

Tee as a hack T恤作为一个黑客

In the situation your question describes, using tee is a hack because we're ignoring half of what it does .在您的问题所描述的情况下,使用tee是一种黑客行为,因为我们忽略了它所做的一半 sudo tee writes to our file and also sends the buffer contents to standard output, but we ignore standard output . sudo tee写入我们的文件并将缓冲区内容发送到标准输出,但我们忽略标准输出 We don't need to pass anything to another piped command in this case;在这种情况下,我们不需要将任何东西传递给另一个管道命令; we're just using tee as an alternate way of writing a file and so that we can call it with sudo .我们只是使用tee作为编写文件的另一种方式,以便我们可以使用sudo调用它。

Making this trick easy使这个技巧变得容易

You can add this to your .vimrc to make this trick easy-to-use: just type :w!!你可以将它添加到你的.vimrc ,使这个技巧易于使用:只需输入:w!! . .

" Allow saving of files as sudo when I forgot to start vim using sudo.
cmap w!! w !sudo tee > /dev/null %

The > /dev/null part explicitly throws away the standard output, since, as I said, we don't need to pass anything to another piped command. > /dev/null部分明确丢弃标准输出,因为正如我所说,我们不需要将任何内容传递给另一个管道命令。

In the executed command line, % stands for the current file name .在执行的命令行中, %代表当前文件名 This is documented in :help cmdline-special :这记录在: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.

As you've already found out, :w !cmd pipes the contents of the current buffer to another command.正如您已经发现的那样, :w !cmd将当前缓冲区的内容通过管道传递给另一个命令。 What tee does is copy standard input to one or more files, and also to standard output. tee作用是将标准输入复制到一个或多个文件,也复制到标准输出。 Therefore, :w !sudo tee % > /dev/null effectively writes the contents of the current buffer to the current file while being root .因此, :w !sudo tee % > /dev/null有效地将当前缓冲区的内容写入当前文件,同时是 root Another command that can be used for this is dd :可用于此的另一个命令是dd

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

As a shortcut, you can add this mapping to your .vimrc :作为一种快捷方式,您可以将此映射添加到您的.vimrc

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

With the above you can type :w!!<Enter> to save the file as root.使用上面的命令,您可以输入:w!!<Enter>将文件保存为 root。

This also works well:这也很有效:

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

This is inspired by the comment of @Nathan Long.这是受到@Nathan Long 评论的启发。

NOTICE :注意

" must be used instead of ' because we want % to be expanded before passing to shell. "必须使用而不是'因为我们希望%在传递给 shell 之前被扩展。

:w - Write a file. :w - 写一个文件。

!sudo - Call shell sudo command. !sudo - 调用 shell sudo 命令。

tee - The output of write (vim :w) command redirected using tee. tee - 使用 tee 重定向的 write (vim :w) 命令的输出。 The % is nothing but current file name ie /etc/apache2/conf.d/mediawiki.conf. % 只是当前文件名,即 /etc/apache2/conf.d/mediawiki.conf。 In other words tee command is run as root and it takes standard input and write it to a file represented by %.换句话说,tee 命令以 root 身份运行,它接受标准输入并将其写入由 % 表示的文件。 However, this will prompt to reload file again (hit L to load changes in vim itself):但是,这将提示再次重新加载文件(点击 L 以加载 vim 本身的更改):

tutorial link 教程链接

The accepted answer covers it all, so I'll just give another example of a shortcut that I use, for the record.接受的答案涵盖了所有内容,因此我将仅举另一个我使用的快捷方式示例,以供记录。

Add it to your etc/vim/vimrc (or ~/.vimrc ):将它添加到您的etc/vim/vimrc (或~/.vimrc ):

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

Where:在哪里:

  • cnoremap : tells vim that the following shortcut is to be associated in the command line. cnoremap : 告诉vim在命令行中关联以下快捷方式。
  • w!! : the shortcut itself. : 快捷方式本身。
  • execute '...' : a command that execute the following string. execute '...' :执行以下字符串的命令。
  • silent! : run it silently : 静默运行
  • write !sudo tee % >/dev/null : the OP question, added a redirection of messages to NULL to make a clean command write !sudo tee % >/dev/null :OP 问题,添加了消息重定向到NULL以创建一个干净的命令
  • <bar> edit! : this trick is the cherry of the cake: it calls also the edit command to reload the buffer and then avoid messages such as the buffer has changed . :这个技巧是锦上添花:它还调用edit命令来重新加载缓冲区,然后避免诸如缓冲区已更改之类的消息。 <bar> is how to write the pipe symbol to separate two commands here. <bar>是这里如何写管道符号来分隔两个命令。

Hope it helps.希望能帮助到你。 See also for other problems:另请参阅其他问题:

I'd like to suggest another approach to the "Oups I forgot to write sudo while opening my file" issue:我想建议另一种方法来解决“我在打开文件时忘记写sudo问题的另一种方法:

Instead of receiving a permission denied , and having to type :w!!而不是收到permission denied ,而必须键入:w!! , I find it more elegant to have a conditional vim command that does sudo vim if file owner is root . ,我发现如果文件所有者是root ,则拥有一个执行sudo vim的条件vim命令更优雅。

This is as easy to implement (there might even be more elegant implementations, I'm clearly not a bash-guru):这很容易实现(甚至可能有更优雅的实现,我显然不是 bash-guru):

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

And it works really well.而且效果很好。

This is a more bash -centered approach than a vim -one so not everybody might like it.这是一种比vim更以bash中心的方法,所以不是每个人都喜欢它。

Of course:当然:

  • there are use cases where it will fail (when file owner is not root but requires sudo , but the function can be edited anyway)有些用例会失败(当文件所有者不是root但需要sudo ,但无论如何都可以编辑该函数)
  • it doesn't make sense when using vim for reading-only a file (as far as I'm concerned, I use tail or cat for small files)使用vim只读文件时没有意义(就我而言,我对小文件使用tailcat

But I find this brings a much better dev user experience , which is something that IMHO tends to be forgotten when using bash .但我发现这带来了更好的开发用户体验,恕我直言,使用bash时往往会忘记这一点。 :-) :-)

FOR NEOVIM对于NEOVIM

Due to problems with interactive calls ( https://github.com/neovim/neovim/issues/1716 ), I am using this for neovim, based on Dr Beco's answer:由于交互式调用( 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!

This will open a dialog using ssh-askpass asking for the sudo password.这将使用ssh-askpass打开一个对话框,要求输入 sudo 密码。

A summary (and very minor improvement) on the most common answers that I found for this as at 2020.我在 2020 年为此找到的最常见答案的摘要(和非常小的改进)。

tl;dr tl;博士

Call with :w!!呼叫:w!! or :W!!:W!! . . After it expands, press enter .展开后,按enter

  • If you are too slow in typing the !!如果你打字太慢!! after the w/W, it will not expand and might report: E492: Not an editor command: W!!在 w/W 之后,它不会扩展并可能报告: E492: Not an editor command: W!!

NOTE Use which tee output to replace /usr/bin/tee if it differs in your case.注意如果您的情况不同,请使用which tee输出来替换/usr/bin/tee

Put these in your ~/.vimrc file:把这些放在你的~/.vimrc文件中:

    " Silent version of the super user edit, sudo tee trick.
    cnoremap W!! execute 'silent! write !sudo /usr/bin/tee "%" >/dev/null' <bar> edit!
    " Talkative version of the super user edit, sudo tee trick.
    cmap w!! w !sudo /usr/bin/tee >/dev/null "%"

More Info:更多信息:

First, the linked answer below was about the only other that seemed to mitigate most known problems and differ in any significant way from the others.首先,下面的链接答案是关于唯一一个似乎减轻了大多数已知问题并且与其他答案有任何显着差异的答案。 Worth reading: https://stackoverflow.com/a/12870763/2927555值得一读: https : //stackoverflow.com/a/12870763/2927555

My answer above was pulled together from multiple suggestions on the conventional sudo tee theme and thus very slightly improves on the most common answers I found.我上面的答案是从关于传统 sudo tee 主题的多个建议中提取出来的,因此对我发现的最常见的答案略有改进。 My version above:我上面的版本:

  • Works with whitespace in file names使用文件名中的空格

  • Mitigates path modification attacks by specifying the full path to tee.通过指定 tee 的完整路径来减轻路径修改攻击。

  • Gives you two mappings, W!!给你两个映射,W!! for silent execution, and w!!静默执行,w !! for not silent, ie Talkative :-)因为不沉默,即健谈:-)

  • The difference in using the non-silent version is that you get to choose between [O]k and [L]oad.使用非静音版本的不同之处在于您可以在 [O]k 和 [L]oad 之间进行选择。 If you don't care, use the silent version.如果您不在乎,请使用静音版本。

    • [O]k - Preserves your undo history, but will cause you to get warned when you try to quit. [O]k - 保留您的撤消历史,但会在您尝试退出时收到警告。 You have to use :q!你必须使用 :q! to quit.退出。
    • [L]oad - Erases your undo history and resets the "modified flag" allowing you to exit without being warned to save changes. [L]oad - 删除您的撤消历史并重置“修改标志”,允许您退出而不会被警告保存更改。

Information for the above was drawn from a bunch of other answers and comments on this, but notably:以上信息来自一系列其他答案和对此的评论,但值得注意的是:

Dr Beco's answer: https://stackoverflow.com/a/48237738/2927555 Beco 博士的回答: https : //stackoverflow.com/a/48237738/2927555

idbrii's comment to this: https://stackoverflow.com/a/25010815/2927555 idbrii 对此的评论: https ://stackoverflow.com/a/25010815/2927555

Han Seoul-Oh's comment to this: How does the vim "write with sudo" trick work? Han Seoul-Oh 对此的评论: vim“用 sudo 编写”技巧如何工作?

Bruno Bronosky comment to this: https://serverfault.com/a/22576/195239 Bruno Bronosky 对此的评论: https ://serverfault.com/a/22576/195239

This answer also explains why the apparently most simple approach is not such a good idea: https://serverfault.com/a/26334/195239这个答案也解释了为什么显然最简单的方法不是一个好主意: https : //serverfault.com/a/26334/195239

The only problem with cnoremap w!! cnoremap w!!的唯一问题cnoremap w!! is that it replaces w with !是它将w替换为! (and hangs until you type the next char) whenever you type w! (并挂起直到您输入下一个字符)每当您输入w! at the : command prompt.:命令提示符下。 Like when you want to actually force-save with w!就像当你想用w!强制保存时一样w! . . Also, even if it's not the first thing after : .此外,即使这不是:之后的第一件事。

Therefore I would suggest mapping it to something like <Fn>w .因此,我建议将其映射到<Fn>w类的东西。 I personally have mapleader = F1, so I'm using <Leader>w .我个人有 mapleader = F1,所以我使用<Leader>w

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM