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

发布于 08-27 23:47 字数 802 浏览 9 评论 0原文

你们中的许多人可能都见过这个命令,它允许您在需要 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 有人可以帮我解析这个命令吗?

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:

: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_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.

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.

tl;dr Could someone help me dissect this command?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

滴情不沾2024-09-03 23:47:42

:w !sudo tee %...

% 表示“当前文件”

As eugene y 指出%确实意味着“当前文件名”,它被传递给tee以便它知道要覆盖哪个文件。

(在替换命令中,它略有不同;如:help :%所示,它等于1,$(整个文件)(感谢@Orafu指出这不会计算为文件名),例如,:%s/foo/bar 表示“在当前文件中,替换出现的 foo 和 bar。”如果您在输入 :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

对于 tee,将 tee 命令想象为正常 bash 管道情况下的 T 形管道:它将输出定向到指定的文件和 还将其发送到标准输出,可以由下一个管道命令捕获。

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

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

(使用 Asciiflow 创建的图表。)

请参阅 tee 手册页 了解更多信息。

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

In :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.

(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.)

:w isn't updating your file

One confusing part of this trick is that you might think :w is modifying your file, but it isn't. If you opened and modified file1.txt, then ran :w file2.txt, it would be a "save as"; file1.txt wouldn't be modified, but the current buffer contents would be sent to file2.txt.

Instead of file2.txt, you can substitute a shell command to receive the buffer contents. For instance, :w !cat will just display the contents.

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. In this case, we use 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.

For example, in ps -ax | tee processes.txt | grep 'foo', the list of processes will be written to a text file and passed along to grep.

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

(Diagram created with Asciiflow.)

See the tee man page for more info.

Tee as a hack

In the situation your question describes, using tee is a hack because we're ignoring half of what it does. sudo tee writes to our file and also sends the buffer contents to standard output, but we ignore standard output. 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.

Making this trick easy

You can add this to your .vimrc to make this trick easy-to-use: just type :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.

始终不够2024-09-03 23:47:42

在执行的命令行中,%代表当前文件名。这记录在 :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!! 将文件保存为 root。

In the executed command line, % stands for the current file name. This is documented in :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. What tee does is copy standard input to one or more files, and also to standard output. Therefore, :w !sudo tee % > /dev/null effectively writes the contents of the current buffer to the current file while being root. Another command that can be used for this is dd:

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

As a shortcut, you can add this mapping to your .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.

半衾梦2024-09-03 23:47:42

已接受的答案涵盖了所有内容,因此我仅给出我使用的快捷方式的另一个示例,以供记录。

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

  • cnoremap w!!执行'沉默!写 !sudo tee % >/dev/null'; edit!

其中:

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

希望有帮助。另请参阅其他问题:

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):

  • 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.
  • w!!: the shortcut itself.
  • execute '...': a command that execute the following string.
  • silent!: run it silently
  • write !sudo tee % >/dev/null: the OP question, added a redirection of messages to NULL to make a clean command
  • <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. <bar> is how to write the pipe symbol to separate two commands here.

Hope it helps. See also for other problems:

断爱2024-09-03 23:47:42

这也很有效:

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

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

注意

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

This also works well:

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

This is inspired by the comment of @Nathan Long.

NOTICE:

" must be used instead of ' because we want % to be expanded before passing to shell.

好倦2024-09-03 23:47:42

:w - 写入文件。

!sudo - 调用 shell sudo 命令。

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

教程链接

:w - Write a file.

!sudo - Call shell sudo command.

tee - The output of write (vim :w) command redirected using tee. The % is nothing but current file name i.e. /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 %. However, this will prompt to reload file again (hit L to load changes in vim itself):

tutorial link

美人迟暮2024-09-03 23:47:42

我想建议另一种方法来解决“哦,我在打开文件时忘记写 sudo 问题:

而不是收到权限被拒绝,并且必须输入 :w!!,我发现使用条件 vim 命令来执行 sudo vim if fileowner 更优雅是

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

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

而且它工作得非常好。

这是一种比 vim 更以 bash 为中心的方法,因此并不是每个人都喜欢它。

当然:

  • 在某些用例中它会失败(当文件所有者不是 root 但需要 sudo 时,但该函数无论如何都可以编辑),
  • 这是没有意义的当使用 vim 只读文件时(就我而言,我使用 tailcat 小文件)

但是我发现这带来了更好的开发用户体验,恕我直言,在使用bash时往往会忘记这一点。 :-)

I'd like to suggest another approach to the "Oups I forgot to write sudo while opening my file" issue:

Instead of receiving a permission denied, and having to type :w!!, I find it more elegant to have a conditional vim command that does sudo vim if file owner is root.

This is as easy to implement (there might even be more elegant implementations, I'm clearly not a 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.

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)
  • 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)

But I find this brings a much better dev user experience, which is something that IMHO tends to be forgotten when using bash. :-)

遇见了你2024-09-03 23:47:42

对于 NEOVIM

由于交互式调用出现问题 (https://github.com /neovim/neovim/issues/1716),我将其用于 neovim,基于 Beco 博士的回答:

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

这将使用 ssh-askpass 打开一个对话框,询问 sudo 密码。

FOR 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:

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.

简单气质女生网名2024-09-03 23:47:42

截至 2020 年,我为此找到的最常见答案的摘要(以及非常小的改进)。

tl;dr

使用 :w!!:W!! 进行调用。
展开后,按enter

  • 如果您在 w/W 之后输入 !! 的速度太慢,它不会展开并可能报告: E492: Not an editor command: W!!

注意如果与您的情况不同,请使用which tee输出来替换/usr/bin/tee

将它们放入您的 ~/.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 "%"

更多信息:

首先,下面的链接答案是关于唯一另一个似乎可以缓解大多数已知问题并且与其他问题有任何显着差异的答案。值得一读:
https://stackoverflow.com/a/12870763/2927555

我上面的答案是从传统 sudo 的多个建议中总结出来的tee 主题,因此对我发现的最常见答案略有改进。我的上述版本:

  • 使用文件名中的空格

  • 通过指定 tee 的完整路径来缓解路径修改攻击。

  • 给你两个映射,W!!为了无声执行,w!!不沉默,即健谈:-)

  • 使用非静音版本的区别在于,您可以在 [O]k 和 [L]oad 之间进行选择。如果您不在乎,请使用静音版本。

    • [O]k - 保留您的撤消历史记录,但会导致您在尝试退出时收到警告。你必须使用 :q!退出。
    • [L]oad - 擦除您的撤消历史记录并重置“已修改标志”,让您退出时不会收到保存更改的警告。

上述信息来自大量其他答案和评论,但值得注意的是:

Beco 博士的答案:https://stackoverflow。 com/a/48237738/2927555

idbrii对此的评论:https://stackoverflow.com/a/25010815/2927555< /a>

HanSeoul-Oh 对此的评论:vim“用 sudo 写入”技巧如何工作?

Bruno Bronosky 评论对此: https://serverfault.com/a/22576/195239

这个答案也解释了为什么显然是最简单的方法这不是一个好主意:
https://serverfault.com/a/26334/195239

A summary (and very minor improvement) on the most common answers that I found for this as at 2020.

tl;dr

Call with :w!! or :W!!.
After it expands, press 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!!

NOTE Use which tee output to replace /usr/bin/tee if it differs in your case.

Put these in your ~/.vimrc file:

    " 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

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. My version above:

  • Works with whitespace in file names

  • Mitigates path modification attacks by specifying the full path to tee.

  • Gives you two mappings, W!! for silent execution, and w!! for not silent, i.e Talkative :-)

  • The difference in using the non-silent version is that you get to choose between [O]k and [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. You have to use :q! to quit.
    • [L]oad - Erases your undo history and resets the "modified flag" allowing you to exit without being warned to save changes.

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

idbrii's comment to this: https://stackoverflow.com/a/25010815/2927555

Han Seoul-Oh's comment to this: How does the vim "write with sudo" trick work?

Bruno Bronosky comment to this: 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

梦途2024-09-03 23:47:42

cnoremap w!! 的唯一问题是,每当您键入 <在 : 命令提示符下输入 code>w!。就像当您想使用 w! 实际强制保存时一样。另外,即使它不是 : 之后的第一件事。

因此,我建议将其映射为 w 之类的内容。我个人有mapleader = F1,所以我使用w

The only problem with cnoremap w!! is that it replaces w with ! (and hangs until you type the next char) whenever you type w! at the : command prompt. Like when you want to actually force-save with w!. Also, even if it's not the first thing after :.

Therefore I would suggest mapping it to something like <Fn>w. I personally have mapleader = F1, so I'm using <Leader>w.

︶ ̄淡然2024-09-03 23:47:42

所以,当你意识到忘记使用全能的 sudo 打开 Vim 时,这个漂亮的命令 :w !sudo tee % 就像一个小解决方法。

让我们分解一下:

:w 是 Vim 表达“保存”的方式。这就像在常规文本编辑器中按 Ctrl + S 一样。

!就像召唤大炮一样 - 它可以让你从 Vim 内部运行 shell 命令。

sudo 是一个神奇的词,它可以赋予您超级用户的权力。它可以让您执行需要管理员权限的操作。

Tee 就像一个多任务处理者。它接受输入并将其发送到一个或多个文件,并将其输出到屏幕上。

% 是当前文件名的占位符。因此,如果您正在编辑“superimportant.txt”,% 将变为“superimportant.txt”。

把它们放在一起, :w !sudo tee % 就像在说,“嘿,保存这个文件,但是用超级用户 mojo 来做!”它将您正在处理的内容交给 tee,然后 tee 使用 sudo 将其传递到文件。这样,您就可以保存对需要管理员权限的文件的更改,即使您没有使用这些权限启动 Vim。

您可以阅读有关在权限被拒绝时保存编辑的更多信息 这里

So, this nifty command :w !sudo tee % is like a little workaround for when you realize you forgot to open Vim with the all-powerful sudo.

Let's break it down:

:w is Vim's way of saying "save". It's like hitting Ctrl + S in a regular text editor.

! is like calling in the big guns - it lets you run a shell command from inside Vim.

sudo is the magic word that grants you superuser powers. It lets you do things that require admin privileges.

tee is like a multitasker. It takes input and sends it to one or more files and also spits it out to the screen.

% is a placeholder for the current file's name. So, if you're editing "superimportant.txt", % becomes "superimportant.txt".

Putting it all together, :w !sudo tee % is like saying, "Hey, save this file, but do it with superuser mojo!" It takes what you're working on, hands it off to tee, which then passes it to the file using sudo. This way, you can save changes to a file that demands admin permissions, even if you didn't start Vim with those powers.

You can read more about saving edit when getting permission denied here

烟─花易冷2024-09-03 23:47:42

在 Neovim 中不起作用,因为 shell 存在要求内联密码的问题。在幕后我认识到 C readline() 函数使用转义字符做自己奇怪的事情。

但是,要强制将密码传递到 sudo 中,我们可以将密码输入 STDIN,如下所示:

:w !sudo tee % <<<< mypassword

警告你的密码将会以明文形式写入 vim 历史记录中。

Won't work in Neovim, because the shell has issues asking for a password inline. Under the hood i recognize the C readline() function that does its own weird things with escape characters.

But, to force passing a password into sudo we could feed the password into the STDIN, as bellow:

:w !sudo tee % <<< mypassword

Warning your password will be written clear text into the vim history.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文