许多人可能已经看到允许您在需要根权限的文件上写入的命令,即使您忘记用sudo打开vim:
问题是我不知道这里到底发生了什么。
我已经想好了:w是为了这个
1 2 3 4 5 6
| *: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的参数),但在帮助中找不到此行为的引用。
有人能帮我解剖这个命令吗?
- @内森:江户十一〔0〕会不会也起作用,不会污染标准产量?
- @不,那不行。在这种情况下,sudo适用于cat,而不适用于>,因此是不允许的。您可以尝试在sudo子shell中运行整个命令,如:w !sudo sh -c"cat % > yams.txt",但这也不起作用,因为在子shell中,%为零;您将清空文件的内容。
- @内森龙:你也许可以用:w !sudo sh -c 'cat $1 > yams.txt' - %,但我不知道它如何处理特殊字符或空格。
- 我只想在输入该命令后添加一条警告消息。如果是,请按L。然后,系统将要求您按Enter。这样,您就可以保存文件了。
- @帕萨:已经有人提出了,但被拒绝了:sudo适用于cat而不是>。
- @nathanlong@knittl::w !sudo sh -c"cat >%"实际上和sudo tee %一样有效,因为vim在进入子shell之前将文件名替换为%。但是,如果文件名中有空格,它们都不起作用;您必须执行:w !sudo sh -c"cat >'%'"或:w !sudo tee"%"来修复这个问题。
- 使用:w保存并重新加载文件:命令w:execute':无提示w!sudo tee%>/dev/null':编辑!
- 注意,这个技巧目前不适用于neovim。
在:w !sudo tee %中…
%表示"当前文件"
正如尤金Y所指出的,%确实意味着"当前文件名"。VIM中的另一个用途是替换命令。例如,:%s/foo/bar的意思是"在当前文件中,用bar替换出现的foo",如果在键入:s之前突出显示一些文本,您会看到突出显示的行取代了%作为替换范围。
:w没有更新你的文件
这个技巧的一个令人困惑的部分是,您可能认为:w正在修改您的文件,但它不是。如果您打开并修改file1.txt,然后运行:w file2.txt,它将是一个"另存为";file1.txt不会被修改,但当前的缓冲区内容将被发送到file2.txt。
您可以用shell命令代替file2.txt来接收缓冲区内容。例如,:w !cat只显示内容。
如果vim不是用sudo access运行的,那么它的:w不能修改受保护的文件,但是如果它将缓冲区内容传递给shell,那么shell中的命令可以用sudo运行。在这种情况下,我们使用tee。
了解TEE
对于tee,将tee命令想象成正常bash管道情况下的T形管道:它将输出定向到指定的文件,并将其发送到标准输出,然后由下一个管道命令捕获。
例如,在ps -ax | tee processes.txt | grep 'foo'中,进程列表将写入一个文本文件并传递给grep。
1 2 3 4 5 6 7 8 9 10 11
| +-----------+ tee +------------+
| | -------- | |
| ps -ax | -------- | grep 'foo' |
| | || | |
+-----------+ || +------------+
||
+---------------+
| |
| processes.txt |
| |
+---------------+ |
(使用asciiFlow创建的图表。)
有关更多信息,请参阅tee手册页。
三通
在您的问题描述的情况下,使用tee是一种黑客行为,因为我们忽略了它的一半功能。sudo tee写入我们的文件,并将缓冲区内容发送到标准输出,但我们忽略了标准输出。在本例中,我们不需要将任何内容传递给另一个管道命令;我们只是使用tee作为另一种编写文件的方法,这样我们就可以使用sudo来调用它。
让这个把戏简单点
你可以将这个添加到你的.vimrc中,使这个技巧易于使用:只需输入:w!!。
1 2
| " Allow saving of files as sudo when I forgot to start vim using sudo.
cmap w!! w !sudo tee > /dev/null % |
> /dev/null部分明确地丢弃了标准输出,因为正如我所说,我们不需要将任何内容传递给另一个管道命令。
- 特别喜欢你的符号"W!!"使用"sudo"后很容易记住!!"在命令行上。
- 有没有一种方法可以为这种情况定义命令,例如:sudow?
- @白壁虎他已经做到了。cmap将命令映射到动作。
- 因此,它使用tee来实现将stdin写入文件的能力。我很惊讶没有一个程序能做到这一点(我发现了一个我从未听说过的程序,叫做sponge)。我猜典型的"将流写入文件"是由一个内置的shell执行的。Vim的!{cmd}不是叉壳吗(而叉cmd)?也许更明显的是使用一些工作变体的sh -c">",而不是tee。
- @Steven Lu:sponge是moreutils包的一部分,除了基于debian的发行版,几乎每个发行版都有。moreutils有一些非常好的工具,可以与更常见的工具(如xargs和tee媲美。
- 如何扩展这个别名来告诉Vim自动将更改的文件内容加载到当前缓冲区?它要求我这样做,如何使它自动化?
- 你的CMAP不适合我。但事实是这样的:快!W!sudo tee%>/dev/null
- 我想知道为什么:W!那么sudo cat>%不起作用了?
- @用户247077:在这种情况下,cat作为根运行,输出由shell重定向,shell不作为根运行。与echo hi > /read/only/file相同。
- @Warfox认为这个分数对stackoverflow.com/a/1732454/4376更合适。
- 这个伟大的解释遗漏了对sudoedit.vim插件的引用。
- ":%s/foo/bar的意思是‘在当前文件中,…’"—哈。我总是从视觉上看,比如"0从上到0从下"
- 这是映射一个命令行模式键序列,在键入:w时,该键序列将引入扫描暂停。您可以考虑将用户定义的命令添加到您的.vimrccommand! Wsudo w !sudo tee > /dev/null % 中。然后可以调用为:Wsudo。
- 现在我理解了为什么在使用这个"hack"时,它在返回vim之前输出while文件。谢谢你的解释。
- 江户十一〔十〕号命令的解释是不可能的。纯粹的辉煌!
在执行的命令行中,%代表当前文件名。这在:help cmdline-special中有记录:
1 2 3
| 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在作为根目录的同时,有效地将当前缓冲区的内容写入当前文件。另一个可用于此目的的命令是dd:
1
| :w !sudo dd of=% > /dev/null |
作为快捷方式,您可以将此映射添加到您的.vimrc中:
1 2
| " Force saving files that require root permission
cnoremap w!! w !sudo tee > /dev/null % |
通过上述方法,您可以键入:w!!将文件保存为根文件。
- 有趣的是,:help _%会显示您输入的内容,但:help %会显示大括号匹配键。我不想尝试使用下划线前缀,这是VIM文档中的某种模式吗?在寻求帮助时,还有什么"特别"的事情要尝试吗?
- @大卫:help命令跳到一个标签上。您可以看到带有:h help-tags的可用标签。您还可以使用命令行完成来查看匹配的标记::h cmdline(如果您相应地设置wildmode,则可以使用:h cmdline)
- 我不得不在.vimrc文件中使用cmap w!! w !sudo tee % > /dev/null来完成这项工作。上述答案中的%是否放错了位置?(这里没有VIM专家。)
- @是的,是的。答案中的命令将导致sudo tee > /dev/null /path/to/current/file,这并不真正有意义。(将要编辑)
- @你错了。shell实际上并不关心在命令行上执行文件重定向的位置。
- @尤金妮,哦,对不起,我不知道这行得通。它只在将tee管插入中间时起作用。奇怪的是,它对@dmfll不起作用。
这也很有效:
这是受到@nathan long评论的启发。
注意事项:
必须使用"而不是',因为我们希望在传递给shell之前扩展%。
- 虽然这可能有效,但它也允许sudo访问多个程序(sh和cat)。其他例子可以更安全地用/usr/bin/tee替换tee,以防止路径修改攻击。
:w—写一个文件。
!sudo—调用shell sudo命令。
tee—使用tee重定向的写入(vim:w)命令的输出。%只是当前文件名,即/etc/apache2/conf.d/mediawiki.conf。换句话说,tee命令作为根目录运行,它接受标准输入并将其写入由%表示的文件。但是,这将再次提示重新加载文件(单击l以加载VIM本身的更改):
教程链接
被接受的答案涵盖了这一切,所以我将给出另一个我用来记录的快捷方式的例子。
添加到你的etc/vim/vimrc或~/.vimrc中:
- cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' edit!
在哪里?
- cnoremap:告诉vim以下快捷方式将在命令行中关联。
- w!!:捷径本身。
- execute '...':执行以下字符串的命令。
- silent!:安静地运行
- write !sudo tee % >/dev/null:操作问题,在NULL中增加了消息的重定向,以生成一个干净的命令。
- edit!:这个技巧是关键所在:它还调用edit命令重新加载缓冲区,然后避免缓冲区等消息发生更改。是如何在这里编写管道符号来分隔两个命令的。
希望它有帮助。其他问题请参见:
我想建议另一种方法来解决"在打开我的文件时忘记写sudo"问题:
我发现,如果文件所有者是root,那么有条件的vim命令可以执行sudo vim,而不是接收permission denied,并且必须键入:w!!,这样做更为优雅。
这是很容易实现的(甚至可能有更优雅的实现,我显然不是bash专家):
1 2 3 4 5 6 7 8
| 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只读取一个文件是没有意义的(就我而言,我使用tail或cat只读取小文件)
但我发现这带来了更好的开发用户体验,这是imho在使用bash时容易忘记的。-)
- 只是要知道,这是一个不太宽容。就个人而言,我的大多数错误都是愚蠢的错误。所以当我做一些既愚蠢又重要的事情的时候,我更喜欢保持清醒。这当然是一个偏好问题,但是特权的提升可能是一个认真的行为。另外:如果你经常经历这样的事情,那么就做出":w!!"足够麻烦自动自动sudo(但仅当owner=root时);您可能需要检查当前工作流。
- 有趣的注释,尽管人们应该意识到,因为当文件以root的形式打开时,会查询密码。
- 啊!有区别。这取决于sudo用户是否设置了"nopasswd"。
- 那么没有人会原谅你…:)