如何静默执行shell命令?


70

:!<command>可用于在Shell中执行命令。但这会“接管”我的终端,并用stdout该特定命令填充它。

如何在后台执行仅在非零退出代码时通知我的命令?


1
您愿意使用+ python界面还是其他语言界面之一?
joeytwiddle

1
@joeytwiddle是
OrangeTux

Answers:


52

:silent exec "!command"

注意,在执行命令时,vim会话仍将被占用。这是由于Vim具有同步特性。您仍然可以通过按CTRL + z(将Vim发送到后台)返回外壳,然后fg像往常一样使用命令恢复vim 。

要异步执行操作,请看一下Tim Pope的插件vim-dispatch或项目NeoVim,它具有对异步命令执行的本机支持,您可以使用插件NeoMake轻松利用它。Vim的最新版本还支持异步任务。

:h:无声


5
这也是我看到问题时尝试的第一件事:-)但是,如果您使用输出到stdout(或stderr)的命令,它将“笼罩”您的Vim主窗口(try :silent !ls)...它也不会不会在非0的退出代码上提供正确的输出...因此,我担心它比仅仅使用它会涉及更多:silent……
Martin Tournoij 2015年

哦对不起。在编写注释时添加了一些有关异步执行的注释。不知道如何解决退出状态问题。您可能想在Freenode上的#vim中尝试。
OliverUv 2015年

我还想指出,在带有补丁1-213的Vim 7.4版本中,完全:silent exec "!ls"没有:silent !ls显示任何输出。
OliverUv 2015年

3
嗯,这就是我得到的:silent !lsi.stack.imgur.com/1XvS6.png ...我需要^L再次按一下以修复它
Martin Tournoij 2015年

vim-dispatch当然看起来像是解决给定问题的方法。
joeytwiddle

30

要执行命令而不触发Enter消息,例如:

按ENTER或键入命令以继续

请尝试以下简单示例:

:silent !echo Hello

然后,在返回Vim时按Ctrl+ L(或:redraw!)刷新屏幕。

为了避免刷新,您可以定义自己的自定义命令,例如:

:command! -nargs=1 Silent execute ':silent !'.<q-args> | execute ':redraw!'

现在,您可以使用新的Vim命令运行shell命令(注意:不带且带大写字母S):

:Silent ps

来源:避免在Vim Wikia网站上出现“单击ENTER以继续”提示


1
如何获得非零退出代码的通知?
马丁·图尔诺伊

1
这不是非零退出代码的解决方案,但要添加到此代码中,此Silent命令允许类似:Silent ctags -R . > /dev/null &后台运行的命令。将stdout发送到/ dev / null可以防止输出出现在vim缓冲区中。
ftvs

10

快速而肮脏的解决方案(使用纯Vimscript)

这可以在后台启动一个过程:

:!slow_command_here > /tmp/output 2>&1 &

但是Vim需要一种方法来确定过程何时完成以及如何完成,所以让我们使用标记文件:

:!(rm -f /tmp/finished; slow_command_here > /tmp/output 2>&1; echo "$?" > /tmp/finished) &

现在,我们可以要求Vim经常检查该过程是否已完成:

:augroup WaitForCompletion
:autocmd!
:autocmd CursorHold * if filereadable('/tmp/finished') | exec "augroup WaitForCompletion" | exec "au!" | exec "augroup END" | echo "Process completed with exit code ".readfile('/tmp/finished')[0] | end
:augroup END

嘿,这不漂亮,但是有点奏效!

缺点

不幸的是,直到您移动光标,上面的autocmd才会触发。而且,如果您想一次运行多个后台进程,则需要向这些文件以及autocmd组名称添加唯一的ID。

因此,如果您可以处理额外的依赖关系,则最好使用经过反复测试的解决方案,例如另一个答案中提到的vim-dispatch插件。

显示输出

如果您要查看流程的输出,而不仅仅是出口代码,则将echo上面的最终代码替换为:

silent botright pedit /tmp/output

这将打开预览窗口。要改用quickfix错误列表:

silent cget /tmp/output | copen

快速修复列表使您可以使用轻松导航至错误:cnext。但是,copen当光标打开时,将光标移到quickfix窗口中,这可能令人惊讶/烦恼。

(A为解决方法是打开与QF窗口:copen当最初开始的过程,所以你不会需要在结束调用它。)


2
这是实际上回答问题的唯一答案:“在后台执行仅通知我非零退出代码的命令” ...我不知道为什么其他答案甚至都具有投票权...
马丁·图尔诺伊

2
我相信他们会投票,因为他们可以完美回答问题的标题,这就是将访问者带到此页面的原因。“如何以静默方式执行shell命令?” 常见的SE现象!访客很感激,但可能没有纪律。
joeytwiddle

在理想的世界中,我们可能会有两个独立的问题:“如何以静默方式执行Shell命令?” 和“如何在后台执行shell命令?” 以便Google员工可以找到他们想要的东西。
joeytwiddle

6

在后台运行命令

我正在运行的命令被阻塞了一段时间,而我实际上并不关心输出。可以通过启动不是附加到终端/仿真器而是附加到系统的进程并将所有输出重定向到/ dev / null来解决此问题。例如:

:silent exec "!(espeak 'speaking in background'&) > /dev/null"

(...&)运行时,它在后台和> /dev/null重定向所有输出/dev/null(无)。

括号将输出捕获到子shell(或类似的东西)中,但是它确实具有未附加到当前shell的副作用(没什么大不了的)。

通过映射在后台静默运行命令

我只是意识到,如果实际上在映射它,则可以执行以下操作

nnoremap <silent> <leader>e :!$(espeak 'speaking in the background'&) > /dev/null

上面的命令不会在命令栏中显示任何内容,此外,在vim之外的终端中也不会显示任何内容。它会被映射到<leader> e,这是\ e默认。

运行命令,捕获其输出,显示其内容

(另一种编辑-也许是最整洁的一种)。如果您选择以下命令,它将显示命令的输出


放入新标签:

silent exec "!(echo 'hello. I'm a process! :)') > /tmp/vim_process" | :tabedit /tmp/vim_process

在垂直分割内:

silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :vs /tmp/vim_process

在水平分割内:

silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :sp /tmp/vim_process

... 做你想做的


5

如果您不在乎退出代码,则可以执行以下操作:

:call system('/usr/bin/zathura using-docker.pdf &')

1
如果您关心退出代码,则v:shell_error变量将告诉您
Jerome Dalbert

4

不知道这是否满足您的需求((不像处理中的后台进程foo & –不确定这是否意味着您在“后台”)),但是可以在以下情况中使用自定义命令:

fun! s:PraeceptumTacet(cmd)
    silent let f = systemlist(a:cmd)
    if v:shell_error
        echohl Error
        echom "ERROR #" . v:shell_error
        echohl WarningMsg
        for e in f
            echom e
        endfor
        echohl None
    endif
endfun

command! -nargs=+ PT call s:PraeceptumTacet(<q-args>)

然后使用例如:

:PT ls -l
:PT foobar
ERROR #127
/bin/bash: foobar: command not found

如果您不需要/想要一个简单的错误消息 system()就足够了,然后v:shell_error通过例如进行检查并报告echo Error!

帮助v:shell_error

                                        v:shell_error shell_error-variable
v:shell_error   Result of the last shell command.  When non-zero, the last
                shell command had an error.  When zero, there was no problem.
                This only works when the shell returns the error code to Vim.
                The value -1 is often used when the command could not be
                executed.  Read-only.
                Example: >
    :!mv foo bar
    :if v:shell_error
    :  echo 'could not rename "foo" to "bar"!'
    :endif
                "shell_error" also works, for backwards compatibility.

这并不是在后台真正运行它。您仍然需要等待它完成。
马丁·图尔诺伊

@Carpetsmoker:是的,因此我的评论“……不像……一样处理后台进程foo &。从整个Q文本中,我将其解释为“或”。或者“执行外部命令AKA背景”“执行外部命令_and_作为后台进程。”。我主要根据Q等标题回答-并在“不确定...”上添加了评论-并将其留给OP来说明是或否。
Runium

3

Vim 8引入了工作支持。无需依赖插件即可在后台运行外部命令。例如,要在当前位置运行markdown服务器(markserv)而不阻止vim会话:

:call job_start('markserv .')

这将启动markserv进程作为当前vim进程的子进程。您可以使用进行验证pstree

查看job_start


2

我使用以下代码:

command! -nargs=1 Silent call SilentExec(<q-args>)

function! SilentExec(cmd)
  let cmd = substitute(a:cmd, '^!', '', '')
  let cmd = substitute(cmd, '%', shellescape(expand('%')), '')
  call system(cmd)
endfunction

现在您可以输入以下内容

:Silent !your_command

silent !除了大写字母外,这几乎类似于Vim的内置命令S。它甚至允许可选!,使其看起来更加相似。调用system()使shell命令真正静音:没有屏幕闪烁,也没有重绘。

(如果您需要状态码,则可以检查该v:shell_error变量,有关更多信息,请参见帮助)


1

AsyncRun插件,如果为此设计的,它可以让你在Vim8 / NeoVim后台运行shell命令,并显示在quickfix窗口输出。

就像替换!命令一样:

:AsyncRun ls -la /

您还可以将输出完全隐藏在quickfix窗口中:

:AsyncRun -mode=3 ls -la /

作业完成后,AsyncRun将通过传递一个选项通知您-post

:AsyncRun -post=some_vim_script   ls -la /

定义的vim​​脚本-post将在作业完成后执行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.