Come eseguire silenziosamente i comandi della shell?


70

:!<command>può essere usato per eseguire un comando nella shell. Ma questo "prende il controllo" del mio terminale e lo riempie stdoutdi quel particolare comando.

Come eseguo un comando in background che mi avvisa solo su un codice di uscita diverso da zero?


1
Saresti disposto a utilizzare l'interfaccia + python o una delle altre interfacce linguistiche?
joeytwiddle,

1
@joeytwiddle Sì
OrangeTux,

Risposte:


52

:silent exec "!command"

Nota che la tua sessione vim sarà comunque occupata mentre il tuo comando è in esecuzione. Ciò è dovuto alla natura sincrona di Vim. Puoi ancora tornare alla shell premendo CTRL + z (per inviare Vim in background) e quindi riprendere vim con il comando fgcome al solito.

Per fare le cose in modo asincrono, dai un'occhiata al plugin vim-dispatch di Tim Pope o al progetto NeoVim che ha il supporto nativo per l'esecuzione di comandi asincroni, che puoi facilmente sfruttare usando il plugin NeoMake. L'ultima versione di Vim supporta anche le attività asincrone.

Vedi : h: silenzioso


5
Questa è anche la prima cosa che ho provato quando ho visto la domanda :-) Ma se si utilizza un comando che emette allo stdout (o stderr), sarà "clobber" finestra principale di Vim (provare :silent !ls) ... E 'anche doesn fornire un output corretto su un codice di uscita diverso da 0 ... Quindi temo sia un po 'più :silent
complicato

Oh scusa. Aggiunte alcune note sull'esecuzione asincrona mentre scrivevi il tuo commento. Non so come risolvere il problema dello stato di uscita. Potresti provare in #vim su Freenode.
OliverUv,

Voglio anche sottolineare che né :silent exec "!ls":silent !lsmostrare alcun output a tutti sulla mia versione di Vim, 7.4 con le patch 1-213.
OliverUv,

3
Ecco cosa ottengo dopo :silent !ls: i.stack.imgur.com/1XvS6.png ... Devo premere ^Lper risolverlo di nuovo ...
Martin Tournoij,

vim-dispatch sembra certamente una soluzione al problema dato.
Joeytwiddle,

30

Per eseguire un comando senza attivare il messaggio Invio, come:

Premere INVIO o digitare il comando per continuare

prova il seguente semplice esempio:

:silent !echo Hello

Quindi premere Ctrl+ L(o :redraw!) per aggiornare lo schermo quando si torna a Vim.

Per evitare la necessità di aggiornamento, è possibile definire il proprio comando personalizzato, come:

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

Ora puoi usare il nuovo comando Vim per eseguire un comando shell (nota: senza ! E con la maiuscola S ):

:Silent ps

Fonte: Evitare le istruzioni "Premi INVIO per continuare" sul sito Vim Wikia


1
Come si riceve una notifica con un codice di uscita diverso da zero?
Martin Tournoij,

1
Non una soluzione al codice di uscita diverso da zero, ma per aggiungere a questo, questo comando silenzioso consente comandi come :Silent ctags -R . > /dev/null &eseguire in background. L'invio di stdout a / dev / null impedisce la visualizzazione dell'output nel buffer vim.
ftvs

10

Una soluzione rapida e sporca (in puro Vimscript)

Questo può avviare un processo in background:

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

Ma Vim ha bisogno di un modo per scoprire quando il processo è stato completato e come, quindi usiamo un file marker:

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

Ora possiamo chiedere a Vim di controllare ogni tanto se il processo è stato completato:

: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

Ehi, non è carino, ma in un certo senso funziona!

svantaggi

Sfortunatamente l'autocmd sopra non si attiverà finché non si sposta il cursore. E se si desidera eseguire più di un processo in background alla volta, è necessario aggiungere ID univoci a tali file e al nome del gruppo autocmd.

Quindi, se riesci a gestire una dipendenza aggiuntiva, potresti stare meglio usando una soluzione collaudata come il plugin vim-dispatch menzionato in un'altra risposta.

Visualizzazione dell'output

Se vuoi vedere l' output del processo, piuttosto che solo il codice di uscita , sostituisci il echoprecedente sopra con:

silent botright pedit /tmp/output

Ciò aprirebbe la finestra di anteprima. Per utilizzare invece l'elenco degli errori di correzione rapida:

silent cget /tmp/output | copen

L'elenco delle correzioni rapide ti consente di navigare facilmente verso gli errori utilizzando :cnext. Tuttavia, copenquando si apre, sposta il cursore nella finestra della correzione rapida, il che probabilmente sarà sorprendente / fastidioso.

(Una soluzione per questo potrebbe essere quella di aprire la finestra QF con :copenquando inizialmente iniziare il processo, quindi sarà non necessario chiamare alla fine.)


2
Questa è l'unica risposta qui che in realtà risponde alla domanda: "esegui un comando in background che mi avvisa solo su un codice di uscita diverso da zero" ... Non ho idea del perché le altre risposte abbiano addirittura dei voti ...
Martin Tournoij,

2
Credo che abbiano voti positivi perché rispondono perfettamente al titolo della domanda, che è ciò che porta i visitatori a questa pagina. "Come eseguire silenziosamente i comandi della shell?" Un fenomeno SE comune! I visitatori sono grati, ma forse non disciplinati.
joeytwiddle,

In un mondo ideale, avremmo probabilmente due domande separate: "Come eseguire i comandi della shell in silenzio?" e "Come eseguire i comandi di shell in background?" in modo che i googler possano trovare quello che stanno cercando.
joeytwiddle,

6

Esecuzione di un comando in background

Stavo eseguendo un comando che si bloccava per un po 'e non mi importava molto dell'output. Questo può essere risolto avviando il processo collegato non al terminale / emulatore ma piuttosto al sistema e reindirizzando tutto l'output su / dev / null. Per esempio:

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

lo (...&)esegue in background e > /dev/nullreindirizza tutto l'output su /dev/null(niente).

La parentesi intrappola l'output in una subshell (o qualcosa del genere), ma ha l'effetto collaterale di non essere collegato alla shell corrente (non è un grosso problema).

Esecuzione silenziosa di un comando in background con mappatura

Mi sono appena reso conto che, se lo stai effettivamente mappando, puoi fare qualcosa del genere

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

Quanto sopra non mostrerà nulla nella barra dei comandi e, inoltre, non mostrerà nulla nel terminale al di fuori di VIM. Sarà mappato su <leader> e, che è \ edi default.

Esecuzione di un comando, acquisizione del suo output, visualizzazione del suo contenuto

(Un'altra modifica - e forse la più accurata). Questo mostrerà l' output di un comando se lo scegli:


DENTRO UNA NUOVA SCHEDA:

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

DENTRO UNO SPACCO VERTICALE:

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

DENTRO UNO SPACCATO ORIZZONTALE:

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

... Fai quello che vuoi


5

Se non ti interessa il codice di uscita, puoi procedere con questo:

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

1
Se ti interessa il codice di uscita, la v:shell_errorvariabile ti dirà
Jerome Dalbert,

4

Non sono sicuro che questo soddisfi le tue esigenze (non gestisce i processi in background come in foo & - non sono sicuro che sia quello che intendi con "in background" ), ma un comando personalizzato potrebbe essere usato come 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>)

Quindi utilizzare come ad esempio:

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

Se non è necessario / desiderato il messaggio di errore, system()potrebbe essere sufficiente un semplice , quindi controllare v:shell_errore segnalare ad es echo Error!.

Da aiuto 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.

Questo non lo sta realmente eseguendo in background; devi ancora aspettare che finisca.
Martin Tournoij,

@Carpetsmoker: Sì, quindi il mio commento "... non gestisce i processi in background come in foo &..." . Dal testo Q nel suo insieme l'ho interpretato come uno o. O "Esegui come comando esterno in background AKA" o "Esegui come comando esterno _e_ come processo in background." . Ho risposto principalmente in base al titolo di Q ecc. - e ho aggiunto un commento su "Non sono sicuro ..." - e l'ho lasciato a OP per chiarire se uno o.
Runium,

3

Vim 8 ha introdotto il supporto per i lavori. Si può eseguire un comando esterno in background senza fare affidamento sui plugin. Ad esempio, per eseguire un server markdown ( Markserv ) nella posizione corrente e non bloccare la sessione VIM :

:call job_start('markserv .')

Questo avvia il processo di MarkServ come sottoprocesso dell'attuale processo VIM. Puoi verificarlo con pstree.

Vedi job_start


2

Io uso il seguente codice:

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

Ora puoi digitare quanto segue

:Silent !your_command

Sembra quasi il silent !comando integrato di Vim , tranne la capitale S. Permette anche un optional !per renderlo ancora più simile. La chiamata a system()rendere il comando shell davvero silenzioso: nessuna schermata lampeggia e nessun ridisegno.

(e se hai mai bisogno di un codice di stato, puoi controllare la v:shell_errorvariabile, vedere la guida per maggiori informazioni)


1

Il plug-in AsyncRun, se progettato per questo, consente di eseguire comandi shell in background in Vim8 / NeoVim e visualizzare l'output nella finestra di quickfix.

Proprio come una sostituzione del !comando:

:AsyncRun ls -la /

Puoi anche nascondere completamente l'output nella finestra di quickfix:

:AsyncRun -mode=3 ls -la /

Al termine del lavoro, AsyncRun ti avviserà passando un'opzione -post:

:AsyncRun -post=some_vim_script   ls -la /

Lo script vim definito da -postverrà eseguito al termine del lavoro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.