Como executar comandos shell silenciosamente?

70

:!<command>pode ser usado para executar um comando no shell. Mas isso "assume" meu terminal e o preenche com stdoutesse comando específico.

Como executo um comando em segundo plano que só me notifica em um código de saída diferente de zero?

OrangeTux
fonte
11
Você gostaria de usar a interface + python ou uma das outras interfaces de linguagem?
Joeytwiddle
11
@joeytwiddle Sim
OrangeTux

Respostas:

52

:silent exec "!command"

Observe que sua sessão vim ainda estará ocupada enquanto seu comando estiver em execução. Isso se deve à natureza síncrona do Vim. Você ainda pode voltar ao seu shell pressionando CTRL + z (para enviar o Vim para o segundo plano) e depois retomar o vim com o comando fgnormalmente.

Para fazer as coisas de forma assíncrona, dê uma olhada no plugin vim-dispatch de Tim Pope ou no projeto NeoVim, que possui suporte nativo para execução de comandos assíncronos, que você pode aproveitar facilmente usando o plugin NeoMake. A versão mais recente do Vim também oferece suporte para tarefas assíncronas.

Veja : h: silencioso

OliverUv
fonte
5
Esta é também a primeira coisa que tentei quando vi a pergunta :-) Mas se você usar um comando que produza stdout (ou stderr), ele "derrotará" sua janela principal do Vim (tente :silent !ls) ... Ele também não 't dar saída apropriada em um-0 não código de saída ... Então eu tenho medo é um pouco mais envolvido do que apenas usando :silent...
Martin Tournoij
Oh, desculpe. Adicionadas algumas notas sobre execução assíncrona enquanto você escrevia seu comentário. Não sei como resolver o problema do status de saída. Você pode tentar #vim no Freenode.
OliverUv
Também quero observar que :silent exec "!ls"nem :silent !lsmostro nenhuma saída na minha versão do Vim, 7.4 com os patches 1-213.
19415 OliverUv
3
Hm, aqui está o que eu ganho depois :silent !ls: i.stack.imgur.com/1XvS6.png ... Preciso pressionar ^Lpara corrigi-lo novamente ...
Martin Tournoij
O vim-dispatch certamente parece uma solução para o problema em questão.
Joeytwiddle #
30

Para executar um comando sem acionar a mensagem Enter, como:

Pressione ENTER ou digite o comando para continuar

tente o seguinte exemplo simples:

:silent !echo Hello

Em seguida, pressione Ctrl+ L(ou :redraw!) para atualizar a tela quando voltar ao Vim.

Para evitar a necessidade de atualização, você pode definir seu próprio comando personalizado, como:

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

Agora, você pode usar o novo comando Vim para executar um comando shell (nota: sem ! E com S maiúsculo ):

:Silent ps

Fonte: Evitando os avisos "Pressione ENTER para continuar" no site da Vim Wikia

kenorb
fonte
11
Como você é notificado sobre um código de saída diferente de zero?
Martin Tournoij
11
Não é uma solução para o código de saída diferente de zero, mas para adicionar isso, esse comando Silencioso permite que comandos como :Silent ctags -R . > /dev/null &sejam executados em segundo plano. Enviar stdout para / dev / null impede que a saída apareça no buffer do vim.
Ftvs
10

Uma solução rápida e suja (em puro Vimscript)

Isso pode iniciar um processo em segundo plano:

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

Mas o Vim precisa de uma maneira de descobrir quando o processo foi concluído e como, então, vamos usar um arquivo de marcador:

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

Agora, podemos pedir ao Vim para verificar de vez em quando se o processo foi concluído:

: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

Ei, não é bonito, mas funciona!

Desvantagens

Infelizmente, o autocmd acima não será acionado até você mover o cursor. E se você deseja executar mais de um processo em segundo plano por vez, precisará adicionar IDs exclusivos a esses arquivos e ao nome do grupo autocmd.

Portanto, se você puder lidar com uma dependência extra, é melhor usar uma solução testada e comprovada, como o plug-in vim-dispatch mencionado em outra resposta.

Exibindo saída

Se você deseja ver a saída do processo, em vez de apenas o código de saída , substitua a final echoacima por:

silent botright pedit /tmp/output

Isso abriria a janela de visualização. Para usar a lista de erros do quickfix:

silent cget /tmp/output | copen

A lista de correções rápidas permite navegar facilmente para erros usando :cnext. No entanto, copenmove o cursor para a janela do quickfix quando ela é aberta, o que provavelmente será surpreendente / irritante.

(Uma solução alternativa seria abrir a janela QF :copenao iniciar o processo inicialmente, para que você não precise chamá-lo no final.)

joeytwiddle
fonte
2
Esta é a única resposta aqui que realmente responde à pergunta: "execute um comando em segundo plano que só me notifique em um código de saída diferente de zero" ... Não sei por que as outras respostas ainda têm votos positivos ...
Martin Tournoij
2
Acredito que tenham votos positivos, porque respondem perfeitamente ao título da pergunta, que é o que leva os visitantes a esta página. "Como executar comandos shell silenciosamente?" Um fenômeno comum da SE! Os visitantes são gratos, mas talvez não sejam disciplinados.
Joeytwiddle 12/04/19
Em um mundo ideal, provavelmente teríamos duas perguntas separadas: "Como executar comandos do shell silenciosamente?" e "Como executar comandos shell em segundo plano?" para que os googlers possam encontrar o que estão procurando.
Joeytwiddle 12/04/19
6

Executando um comando em segundo plano

Eu estava executando um comando que bloqueou um pouco e realmente não me importei com a saída. Isso pode ser resolvido iniciando o processo anexado não ao terminal / emulador, mas ao sistema e redirecionando toda a saída para / dev / null. Por exemplo:

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

o (...&)executa em segundo plano e > /dev/nullredireciona toda a saída para /dev/null(nada).

Os parênteses prendem a saída em um subshell (ou algo assim), mas tem o efeito colateral de não ser anexado ao shell atual (não é grande coisa).

Executando um Comando Silenciosamente em Segundo Plano com Mapeamento

Acabei de perceber que, se você estiver mapeando, pode fazer algo como

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

O item acima não exibirá nada na barra de comando e, além disso, não exibirá nada no terminal fora do vim. Será mapeado para <leader> e, que é \ epor padrão.

Executando um comando, capturando sua saída, exibindo seu conteúdo

(Outra edição - e talvez a melhor). Este mostrará a saída de um comando se você escolher:


DENTRO DE UMA NOVA TAB:

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

DENTRO DE UMA DIVISÃO VERTICAL:

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

DENTRO DE UMA FENDA HORIZONTAL:

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

... faça o que você quiser

dylnmc
fonte
5

Se você não se importa com o código de saída, pode fazer isso:

:call system('/usr/bin/zathura using-docker.pdf &')
hsnotebook
fonte
11
Se você se importa com o código de saída, a v:shell_errorvariável informará
Jerome Dalbert 18/18
4

Não tenho certeza se isso atende às suas necessidades (não lida com processos em segundo plano como em foo & - não tenho certeza se é isso que você quer dizer com "em segundo plano" ), mas um comando personalizado pode ser usado como em:

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>)

Então use como por exemplo:

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

Se você não precisa / deseja que a mensagem de erro seja system()suficiente, verifique v:shell_errore relate por exemplo echo Error!.

Na ajuda 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.
Runium
fonte
Isso realmente não está sendo executado em segundo plano; você ainda precisa esperar o término.
Martin Tournoij
@Carpetsmoker: Sim, assim meu comentário “… não lida com processos em segundo plano como em foo &…” . A partir do texto Q como um todo, eu o interpretei como sendo um ou outro. Ou “Executar como comando externo AKA background” ou “Executar como comando externo _e_ como processo em background” . Eu respondi principalmente com base no título de Q etc. - e adicionei um comentário sobre "Não tenho certeza ..." - e deixei para o OP esclarecer se é ou não.
Runium 26/02
3

O Vim 8 introduziu o suporte a trabalhos. Pode-se executar comandos externos em segundo plano sem depender de plug-ins. Por exemplo, para executar um servidor de descontos ( markserv ) no local atual e não bloquear a sessão do vim:

:call job_start('markserv .')

Isso inicia o processo markserv como um subprocesso do processo vim atual. Você pode verificar isso com pstree.

Consulte job_start

KFL
fonte
2

Eu uso o seguinte código:

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

Agora você pode digitar o seguinte

:Silent !your_command

Isso parece quase com o silent !comando interno do Vim , exceto a capital S. Ele ainda permite um opcional !para torná-lo ainda mais semelhante. A chamada para system()torna o comando shell verdadeiramente silencioso: nenhuma tela pisca e não é redesenhada.

(e se você precisar de um código de status, pode verificar a v:shell_errorvariável, consulte a ajuda para obter mais informações)

Jerome Dalbert
fonte
1

O plug- in AsyncRun, se projetado para isso, permite executar comandos do shell em segundo plano no Vim8 / NeoVim e exibir a saída na janela do quickfix.

Apenas como uma substituição do !comando:

:AsyncRun ls -la /

Você também pode ocultar completamente a saída na janela do quickfix:

:AsyncRun -mode=3 ls -la /

Quando o trabalho for concluído, o AsyncRun o notificará passando uma opção -post:

:AsyncRun -post=some_vim_script   ls -la /

O script vim definido por -postserá executado após a conclusão do trabalho.

skywind3000
fonte