很多人可能已经看到了允许你在需要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有人可以帮我剖析这个命令吗?
在:w !sudo tee %
......
%
表示"当前文件"正如eugene指出的那样,%
确实意味着"当前的文件名".Vim中的另一个用途是替换命令.例如,tee
表示" 在当前文件中,替换:help :%
with的出现次数" equal to 1,$ (the entire file)
.如果在键入之前突出显示某些文本:%s/foo/bar
,您会看到突出显示的行代替了foo
替换范围.
bar
没有更新您的文件这个技巧的一个令人困惑的部分是你可能认为:s
正在修改你的文件,但事实并非如此.如果你打开并修改%
,然后运行:w
,它将是"另存为"; :w
不会被修改,但会将当前缓冲区内容发送给file1.txt
.
:w file2.txt
您可以替换shell命令来接收缓冲区内容,而不是.例如,file1.txt
只显示内容.
如果Vim没有使用sudo访问运行,则它file2.txt
无法修改受保护的文件,但如果它将缓冲区内容传递给shell,则可以使用sudo运行shell 中的命令.在这种情况下,我们使用file2.txt
.
至于在正常的bash管道情况下:w !cat
将:w
命令描述为T形管道:它将输出定向到指定的文件,并将其发送到标准输出,可以通过下一个管道命令捕获.
例如,在tee
进程中,进程列表将被写入文本文件并传递给tee
.
+-----------+ tee +------------+ | | -------- | | | ps -ax | -------- | grep 'foo' | | | || | | +-----------+ || +------------+ || +---------------+ | | | processes.txt | | | +---------------+
(使用Asciiflow创建的图表.)
有关详细信息,请参见tee
手册页.
在你的问题描述的情况下,使用ps -ax | tee processes.txt | grep 'foo'
是一个黑客,因为我们忽略了它的一半.grep
写入我们的文件并将缓冲区内容发送到标准输出,但我们忽略标准输出.在这种情况下,我们不需要将任何内容传递给另一个管道命令; 我们只是tee
用作编写文件的替代方式,以便我们可以调用它tee
.
您可以将此添加到您的使用,sudo tee
以使这个技巧易于使用:只需键入tee
.
" Allow saving of files as sudo when I forgot to start vim using sudo. cmap w!! w !sudo tee > /dev/null %
该sudo
部分明确地抛弃了标准输出,因为正如我所说,我们不需要将任何东西传递给另一个管道命令.
在执行的命令行中,%
代表当前文件名.这记录在: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
是将标准输入复制到一个或多个文件,以及标准输出.因此,在root时: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!!
以root身份保存文件.
这也很有效:
:w !sudo sh -c "cat > %"
这是受@Nathan Long评论的启发.
通知:
"
必须使用而不是'
因为我们希望%
在传递给shell之前进行扩展.
:w
- 写一个文件.
!sudo
- 调用shell sudo命令.
tee
- 使用tee重定向的write(vim:w)命令的输出.%只是当前文件名,即/etc/apache2/conf.d/mediawiki.conf.换句话说,tee命令以root身份运行,它接受标准输入并将其写入由%表示的文件.但是,这将提示再次重新加载文件(点击L加载vim本身的更改):
教程链接
可接受的答案涵盖了所有内容,因此,我仅举一个记录所使用的快捷方式的示例。
将其添加到您的etc/vim/vimrc
(或~/.vimrc
)中:
cnoremap w!! execute 'silent! write !sudo tee % >/dev/null'
哪里:
cnoremap
:告诉vim在命令行中要关联以下快捷方式。
w!!
:快捷方式本身。
execute '...'
:执行以下字符串的命令。
silent!
:静默运行
write !sudo tee % >/dev/null
:OP问题,添加了消息重定向NULL
以发出干净命令
:这个技巧是轻而易举的事情:它还调用edit
命令以重新加载缓冲区,然后避免诸如缓冲区已更改的消息。
这里是如何编写管道符号以分隔两个命令的方法。
希望能帮助到你。另请参阅其他问题:
超级用户:强制vim写入文件
我想提出另一种方法来解决“ sudo
打开文件时忘记写的操作”问题:
我发现有一个条件命令(如果文件所有者为的话)可以代替它permission denied
,而不是必须输入。:w!!
vim
sudo vim
root
这很容易实现(甚至可能会有更优雅的实现,我显然不是bash专家):
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
小文件)
但是我发现这带来了更好的开发用户体验,而IMHO在使用时往往会被遗忘bash
。:-)