从Vim中执行Bash当前行

85

这个问题类似于Vim:执行当前文件?,但我想要执行的不是整个文件,而是当前行。

有没有可能实现这个功能?

理想情况下,我希望找到的解决方案可以在外部 shell 中产生副作用。

例如,假设我有以下行:

alias foo=bar

在 Vim 中运行命令后,如果我使用 :sh 启动一个 shell,那么别名 foo 就可以使用了,但是如果我使用 :q 退出 vim,那么这个别名就不再可用。


或许有人认为它属于超级用户。 - ceving
7
这应该是请求移动问题,而不是投反对票吧?或者我误解了stackoverflow的规则?我在SO上发布这个问题是因为SO有一个“vim”标签。 - merlin2011
3
这个问题在我看来没问题,我认为它适合于“程序员常用的软件工具”分类。Vim和Bash正是如此...我不认为这些负评有道理。 - jbat100
很抱歉,无法更改父进程的环境。 (我也不知道如何更改未来shell的环境。) - rici
7个回答

147

当然可以,您可以将当前文件的任何内容“写入”到另一个程序的标准输入(stdin)中:

:.w !bash

这里的. (在w之前的部分)是指你正在编写的行范围,而.只代表当前行。然后你可以使用!bash将这些行写入Bash。


这很棒,但是输出不像:!bash [my commands]那样持久(即我以后可以发出:!并再次查看输出)。有没有办法将其改编为将输出放入_那个_缓冲区域(无论它是什么)? - Alec Jacobson
2
@AlecJacobson 你可以尝试这个:使用 yy 复制当前行,然后使用 :!<Control-r>" 将当前行的内容粘贴到 Vim 命令行中。 - Daan Bakker
1
很好,这对单行代码非常有效。你上面的回答适用于整个选择区域,那么有没有类似于<CTRL-R>的技巧可以应用于多行代码呢?如果我尝试复制多行代码,最终会在一个命令中出现很多^M,而不是多个命令。 - Alec Jacobson
3
我将以下内容添加到~/.vimrc 文件中,将,r 绑定到该命令:nnoremap ,r :.w !bash<CR>。现在在命令模式下,要执行一行,请将光标移到该行并输入, r。输出结果将直接显示在编辑器中。 - wsams
运行.w!sudo bash以 root 权限执行当前行。 - nav
显示剩余2条评论

44

我经常使用以下方式来做这种事情:

:exec '!'.getline('.')

你甚至可以在你的 .vimrc 文件中创建一个映射:

nmap <F6> :exec '!'.getline('.')

3
我喜欢这个答案。你也可以使用 @" 而不是 .getline('.') 来执行复制的文本而不仅仅是一行。 - ws_e_c421

23

将光标移至该行,在正常模式下按:

!!bash<cr>

4
这会不幸地导致当前行被输出结果替换,这并非所期望的。 - merlin2011
2
@merlin2011 如果外部(bash)命令有输出,您不希望在缓冲区中显示它吗?还是您想要将其显示在“当前行”下方? - Kent
1
在这种情况下,我执行该命令是为了产生其副作用,而不是获取其输出。 - merlin2011

9

如果我可以评论,这可能是一条评论。

关于将Vim当前缓冲区的重定向/管线到外部命令,受Daan Bakker的出色答案启发,我在这里写了一篇答案(在Vim中同时保存和运行),回答有关运行Python脚本(当前缓冲区)的问题。

除了运行整个缓冲区之外,还演示了如何通过外部命令运行一系列行。

为了节省时间,我只是将它复制在下面。

#####################

在Vim中,您可以将当前缓冲区的任何范围简单地重定向到外部命令(无论是'bash'、'python'还是您自己的Python脚本)。

# Redirect the whole buffer to 'python'
:%w !python

假设您当前的缓冲区包含以下两行内容:
import numpy as np
print np.arange(12).reshape(3,4)

然后:%w !python会运行它,无论是否保存。并在终端上打印类似以下的内容,
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

当然,您可以使某些内容持久化,例如一些键位映射。
nnoremap <F8> :.w !python<CR>
vnoremap <F8> :w !python<CR>

第一个运行当前行。第二个通过Python解释器运行视觉选择。

#!! Be careful, in Vim ':w!python' and ':.w !python' are very different, the
first writes (creates or overwrites) a file named 'python' with thew contents of
the current buffer. The second redirects the selected cmdline range (here dot .,
which mean current line) to an external command (here 'python').

关于命令行范围,请参见

:h cmdline-ranges

不是下面这个与普通命令有关的,而是与cmdline命令无关的。
:h command-range

这是受到从Vim执行当前行的Bash命令的启发而来。


5

将以下映射添加到您的 .vimrc 文件中:

nmap <leader>E yyp:.!csh<CR>

解释:

  1. yy 复制当前行
  2. p 粘贴已复制的行到下一行(光标移到下一行)
  3. :.!csh<CR> 执行(使用csh)刚刚粘贴的这一行。执行后的输出替换了当前行,因此在执行该命令前,当前行已经被复制并粘贴到下一行。

1
如果我在vi/Vim中有一行文本上的命令,像这样:

ls -la

将光标放在该行的任何位置,然后执行":.!!"并按回车键。

也就是说:冒号 点 感叹号 感叹号 回车

这将在vi/Vim中执行该行文本中的命令,并将输出插入到文本文件中。

我想这就是你所问的吧?就像魔术一样。


0

考虑在终端上运行以下命令,

seq 0 10 | xargs printf "%02x "

期望的输出是,

00 01 02 03 04 05 06 07 08 09 0a

假设您在文件中编写了上述命令。要在该文件中获取相应的输出,请将此映射添加到您的 .vimrc 文件中以执行此命令。

nmap <leader>E :exec 'r!'.getline('.')<CR>

请注意,要执行上述代码行,您需要将其写成添加转义字符 '%' 的形式,如下所示:
seq 0 10 | xargs printf "\%02x "

在你的文件中找到这一行,然后按下<leader>E。在我的情况下,<leader>被映射为,,所以我会按下,E

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接