:!<command>
can be used to execute a command in the shell. But this "takes over" my terminal and fills it with stdout
of that particular command.
How do I execute a command in the background that only notifies me on a non-zero exit code?
Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It only takes a minute to sign up.
Sign up to join this community:!<command>
can be used to execute a command in the shell. But this "takes over" my terminal and fills it with stdout
of that particular command.
How do I execute a command in the background that only notifies me on a non-zero exit code?
:silent exec "!command"
Note that your vim session will still be occupied while your command is executing. This is due to Vim's synchronous nature. You can still get back to your shell by pressing CTRL+z (to send Vim to the background) and then resume vim with the command fg
as usual.
To do things asynchronously, take a look at Tim Pope's plugin vim-dispatch or the project NeoVim which has native support for asynchronous command execution, which you can easily leverage using the plugin NeoMake. The latest version of Vim also has support for asynchronous tasks.
See :h :silent
:silent !ls
) ... It also doesn't give proper output on a non-0 exit code... So I'm afraid it's a bit more involved than just using :silent
...
Feb 19, 2015 at 11:07
:silent exec "!ls"
nor :silent !ls
show any output at all on my version of Vim, 7.4 with patches 1-213.
:silent !ls
: i.stack.imgur.com/1XvS6.png ... I need to press ^L
to fix it again ...
Feb 19, 2015 at 11:35
To execute a command without triggering the Enter message, like:
Press ENTER or type command to continue
try the following simple example:
:silent !echo Hello
Then press Ctrl+L (or :redraw!
) to refresh the screen when back to Vim.
To avoid need for refresh, you can define your own custom command, like:
:command! -nargs=1 Silent execute ':silent !'.<q-args> | execute ':redraw!'
Now, you can use the new Vim command to run a shell command (note: without ! and with capital S):
:Silent ps
Source: Avoiding the "Hit ENTER to continue" prompts at the Vim Wikia site
:Silent ctags -R . > /dev/null &
to run in the background. Sending stdout to /dev/null prevents output from appearing in the vim buffer.
This can start a process in the background:
:!slow_command_here > /tmp/output 2>&1 &
But Vim needs a way to find out when the process has completed, and how, so let's use a marker file:
:!(rm -f /tmp/finished; slow_command_here > /tmp/output 2>&1; echo "$?" > /tmp/finished) &
Now we can ask Vim to check every so often whether the process has completed:
: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
Hey it's not pretty, but it sort-of works!
Unfortunately the autocmd above won't trigger until you move the cursor. And if you wanted to run more than one background process at a time, you would need to add unique IDs to those files, and to the autocmd group name.
So if you can handle an extra dependency, you might be better off using a tried-and-tested solution like the vim-dispatch plugin mentioned in another answer.
If you want to see the output of the process, rather than just the exit code, then replace the final echo
above with:
silent botright pedit /tmp/output
That would open the preview window. To use the quickfix error list instead:
silent cget /tmp/output | copen
The quickfix list lets you easily navigate to errors using :cnext
. However copen
moves your cursor into the quickfix window when it opens, which will probably be surprising/annoying.
(A workaround for that would be to open the QF window with :copen
when initially starting the process, so you will not need to call it at the end.)
I was running a command that blocked for a bit, and I didn't really care about the output. This can be taken care of by starting the process attached not to the terminal/emulator but rather to the system and redirecting all output to /dev/null. For example:
:silent exec "!(espeak 'speaking in background'&) > /dev/null"
the (...&)
runs it in the background and > /dev/null
redirects all output to /dev/null
(nothing).
The parenthesis trap the output into a subshell (or something like that), but it does have the side-effect of not being attached to the current shell (not a big deal).
I just realized that if you are, in fact, mapping it, you can do something like
nnoremap <silent> <leader>e :!$(espeak 'speaking in the background'&) > /dev/null
The above will display nothing in the command bar and will, additionally, not display anything in the terminal outside of vim. It will be mapped to <leader>
e, which is \ e by default.
(Another edit - and maybe the neatest one). This one will display the output of a command if you so choose:
INSIDE A NEW TAB:
silent exec "!(echo 'hello. I'm a process! :)') > /tmp/vim_process" | :tabedit /tmp/vim_process
INSIDE A VERTICAL SPLIT:
silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :vs /tmp/vim_process
INSIDE A HORIZONTAL SPLIT:
silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :sp /tmp/vim_process
... do whatever you want
If you don't care about exit code, you can go with this:
:call system('/usr/bin/zathura using-docker.pdf &')
v:shell_error
variable will tell you
Mar 18, 2018 at 22:22
:!cmd
, :silent! !cmd
, and exec('!cmd')
all either paused till you pressed enter, flashed the screen, or forced you to call :redraw!
. This is the only one that worked completely silently.
Nov 1, 2022 at 22:29
Vim 8 introduced jobs support. One can run external command in the background without relying on plugins. For example, to run a markdown server (markserv) at current location and not block vim session:
:call job_start('markserv .')
This starts markserv process as a sub-process of current vim process. You can verify this with pstree
.
See job_start
jobstart
and it's the only way I was able to spawn an Android Emulator with cold start in the background. Good stuff.
Dec 2, 2022 at 19:26
Not sure if this suits your needs, (does not handle background processes as in foo &
– not sure if that is what you mean by “in the background”), but a custom command could be used as in:
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>)
Then use as e.g.:
:PT ls -l
:PT foobar
ERROR #127
/bin/bash: foobar: command not found
If you do not need / want the error message a simple system()
could suffice, then check for v:shell_error
and report by e.g echo Error!
.
From help 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.
foo &
…”. From the Q text as a whole I interpreted it as being either or. Either “Execute as external command AKA background” or “Execute as external command _and_ as background process.”. I answered mainly based on the title of Q etc. – and added a comment on “Not sure …” – and left it to OP to clarify if either or.
I use the following code:
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
Now you can type the following
:Silent !your_command
This looks almost like Vim's built-in silent !
command, except for the capital S
. It even allows for an optional !
to make it look even more similar. The call to system()
makes the shell command truly silent: no screen flashes and no redraws.
(and if you ever need a status code, you can check the v:shell_error
variable, see help for more info)
The AsyncRun plugin if designed for this, it allows you to run shell commands in the background in Vim8/NeoVim and display the output in the quickfix window.
Just as a replacement of !
command:
:AsyncRun ls -la /
You can also hide the output in quickfix window completely:
:AsyncRun -mode=3 ls -la /
When job finished, AsyncRun will notify you by passing an option -post
:
:AsyncRun -post=some_vim_script ls -la /
The vim script defined by -post
will be executed after job finished.