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:有人能帮我分析一下这个命令吗?
7 个解决方案
#1
1221
%
means "the current file"
As eugene y pointed out, %
does indeed mean "the current file name". Another use for this in Vim is in substitution commands. 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.
正如eugene y所指出的,%确实意味着“当前文件名”。Vim中的另一个用法是替换命令。例如,:%s/foo/bar表示“在当前文件中,用bar替换foo出现的事件”。如果您在键入之前突出显示一些文本:s,您将看到高亮显示的行取代%作为您的替换范围。
: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
.
这个技巧的一个令人困惑的部分是,您可能认为:w正在修改您的文件,但它不是。如果您打开和修改file1。txt,然后运行:w file2。txt,它将是一个“保存为”;file1。txt不会被修改,但是当前的缓冲区内容将被发送到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.
而不是file2。txt,您可以用shell命令来接收缓冲区内容。例如: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. In this case, we use tee
.
如果Vim不是用sudo访问运行的,它的:w不能修改受保护的文件,但是如果它将缓冲区内容传递给shell,那么shell中的命令可以与sudo一起运行。在这种情况下,我们使用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,在正常的bash管道情况下,将tee命令想象成t形管道:它将输出定向到指定的文件,并将其发送到标准输出,这可以由下一个管道命令捕获。
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
.
例如,在ps -ax | tee进程中。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 man”页面。
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
.
在这种情况下,你的问题描述,使用tee是一个hack,因为我们忽略了它所做的一半。sudo tee写入我们的文件,并将缓冲区内容发送到标准输出,但是我们忽略了标准输出。在这种情况下,我们不需要传递任何东西到另一个管道命令;我们只是用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部分显式地丢弃了标准输出,因为正如我所说的,我们不需要将任何东西传递给另一个管道命令。
#2
84
In the executed command line, %
stands for the current file name. This is documented in :help cmdline-special
:
在已执行的命令行中,%表示当前文件名。这是有记录的:帮助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 !cmd将当前缓冲区的内容传输到另一个命令。tee所做的是将标准输入复制到一个或多个文件,以及标准输出。因此,:w !sudo tee % > /dev/null有效地将当前缓冲区的内容写入到当前文件的根目录中。另一个可以使用的命令是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!! <输入> 以将文件保存为根。
#3
16
:w
- Write a file.
:w -写一个文件。
!sudo
- Call shell sudo command.
!sudo - Call shell sudo命令。
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):
tee -写入的输出(vim:w)命令重新定向使用tee。%仅为当前文件名,即/etc/apache2/conf.d/mediawiki.conf。换句话说,tee命令是作为根运行的,它接受标准输入并将其写入一个以%为代表的文件。但是,这将提示重新加载文件(单击L以载入vim本身的更改):
教程的链接
#4
15
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之前扩展%”。
#5
1
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!!
, I find it more elegant to have a conditional vim
command that does sudo vim
if file owner is root
.
而不是得到拒绝的许可,并且必须输入:w!!我发现,如果文件所有者是root,那么有条件的vim命令(sudo 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更基础的方法,所以不是每个人都喜欢它。
Of course:
当然,
- there are use cases where it will fail (when file owner is not
root
but requiressudo
, but the function can be edited anyway) - 在某些情况下,它会失败(当文件所有者不是根但需要sudo时,但是这个函数可以被编辑)
- it doesn't make sense when using
vim
for reading-only a file (as far as I'm concerned, I usetail
orcat
for small files) - 使用vim来读取文件是没有意义的(就我而言,我用tail或cat来处理小文件)
But I find this brings a much better dev user experience, which is something that IMHO tends to be forgotten when using bash
. :-)
但我发现这带来了更好的开发用户体验,这是IMHO在使用bash时往往会忘记的东西。:-)
#6
0
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!
-
cnoremap w ! !执行“沉默!写!sudo tee % >/dev/null'
编辑!
Where:
地点:
-
cnoremap
: tells vim that the following shortcut is to be associated in the command line. - cnoremap:告诉vim,以下快捷方式将与命令行相关联。
-
w!!
: the shortcut itself. - w ! !:快捷方式本身。
-
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 toNULL
to make a clean command - 写!sudo tee % >/dev/null: OP问题,将消息的重定向添加到null,以生成一个干净的命令。
-
<bar> edit!
: this trick is the cherry of the cake: it calls also theedit
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. -
<栏> 编辑!:这个技巧是蛋糕的樱桃:它还调用edit命令来重新加载缓冲区,然后避免诸如缓冲区这样的消息发生了变化。
是如何编写管道符号来分隔两个命令。
Hope it helps. See also for other problems:
希望它可以帮助。其他问题参见:
- SuperUser: force vim to write a file
- 超级用户:强制vim写入文件。
#7
0
The above mapping worked fine for me, to save using sudo and to reload the file without pressing additional keys. (just press w)
上面的映射对我来说很好,可以节省使用sudo和重新加载文件而不需要额外的键。(只要按w)
map w :execute ':silent w !sudo tee % > /dev/null' <bar> :edit! <cr>
#1
1221
%
means "the current file"
As eugene y pointed out, %
does indeed mean "the current file name". Another use for this in Vim is in substitution commands. 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.
正如eugene y所指出的,%确实意味着“当前文件名”。Vim中的另一个用法是替换命令。例如,:%s/foo/bar表示“在当前文件中,用bar替换foo出现的事件”。如果您在键入之前突出显示一些文本:s,您将看到高亮显示的行取代%作为您的替换范围。
: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
.
这个技巧的一个令人困惑的部分是,您可能认为:w正在修改您的文件,但它不是。如果您打开和修改file1。txt,然后运行:w file2。txt,它将是一个“保存为”;file1。txt不会被修改,但是当前的缓冲区内容将被发送到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.
而不是file2。txt,您可以用shell命令来接收缓冲区内容。例如: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. In this case, we use tee
.
如果Vim不是用sudo访问运行的,它的:w不能修改受保护的文件,但是如果它将缓冲区内容传递给shell,那么shell中的命令可以与sudo一起运行。在这种情况下,我们使用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,在正常的bash管道情况下,将tee命令想象成t形管道:它将输出定向到指定的文件,并将其发送到标准输出,这可以由下一个管道命令捕获。
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
.
例如,在ps -ax | tee进程中。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 man”页面。
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
.
在这种情况下,你的问题描述,使用tee是一个hack,因为我们忽略了它所做的一半。sudo tee写入我们的文件,并将缓冲区内容发送到标准输出,但是我们忽略了标准输出。在这种情况下,我们不需要传递任何东西到另一个管道命令;我们只是用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部分显式地丢弃了标准输出,因为正如我所说的,我们不需要将任何东西传递给另一个管道命令。
#2
84
In the executed command line, %
stands for the current file name. This is documented in :help cmdline-special
:
在已执行的命令行中,%表示当前文件名。这是有记录的:帮助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 !cmd将当前缓冲区的内容传输到另一个命令。tee所做的是将标准输入复制到一个或多个文件,以及标准输出。因此,:w !sudo tee % > /dev/null有效地将当前缓冲区的内容写入到当前文件的根目录中。另一个可以使用的命令是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!! <输入> 以将文件保存为根。
#3
16
:w
- Write a file.
:w -写一个文件。
!sudo
- Call shell sudo command.
!sudo - Call shell sudo命令。
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):
tee -写入的输出(vim:w)命令重新定向使用tee。%仅为当前文件名,即/etc/apache2/conf.d/mediawiki.conf。换句话说,tee命令是作为根运行的,它接受标准输入并将其写入一个以%为代表的文件。但是,这将提示重新加载文件(单击L以载入vim本身的更改):
教程的链接
#4
15
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之前扩展%”。
#5
1
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!!
, I find it more elegant to have a conditional vim
command that does sudo vim
if file owner is root
.
而不是得到拒绝的许可,并且必须输入:w!!我发现,如果文件所有者是root,那么有条件的vim命令(sudo 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更基础的方法,所以不是每个人都喜欢它。
Of course:
当然,
- there are use cases where it will fail (when file owner is not
root
but requiressudo
, but the function can be edited anyway) - 在某些情况下,它会失败(当文件所有者不是根但需要sudo时,但是这个函数可以被编辑)
- it doesn't make sense when using
vim
for reading-only a file (as far as I'm concerned, I usetail
orcat
for small files) - 使用vim来读取文件是没有意义的(就我而言,我用tail或cat来处理小文件)
But I find this brings a much better dev user experience, which is something that IMHO tends to be forgotten when using bash
. :-)
但我发现这带来了更好的开发用户体验,这是IMHO在使用bash时往往会忘记的东西。:-)
#6
0
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!
-
cnoremap w ! !执行“沉默!写!sudo tee % >/dev/null'
编辑!
Where:
地点:
-
cnoremap
: tells vim that the following shortcut is to be associated in the command line. - cnoremap:告诉vim,以下快捷方式将与命令行相关联。
-
w!!
: the shortcut itself. - w ! !:快捷方式本身。
-
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 toNULL
to make a clean command - 写!sudo tee % >/dev/null: OP问题,将消息的重定向添加到null,以生成一个干净的命令。
-
<bar> edit!
: this trick is the cherry of the cake: it calls also theedit
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. -
<栏> 编辑!:这个技巧是蛋糕的樱桃:它还调用edit命令来重新加载缓冲区,然后避免诸如缓冲区这样的消息发生了变化。
是如何编写管道符号来分隔两个命令。
Hope it helps. See also for other problems:
希望它可以帮助。其他问题参见:
- SuperUser: force vim to write a file
- 超级用户:强制vim写入文件。
#7
0
The above mapping worked fine for me, to save using sudo and to reload the file without pressing additional keys. (just press w)
上面的映射对我来说很好,可以节省使用sudo和重新加载文件而不需要额外的键。(只要按w)
map w :execute ':silent w !sudo tee % > /dev/null' <bar> :edit! <cr>