Comment exécuter des commandes shell en silence?

70

:!<command>peut être utilisé pour exécuter une commande dans le shell. Mais ceci "prend en charge" mon terminal et le remplit avec stdoutcette commande particulière.

Comment puis-je exécuter une commande en arrière-plan qui me notifie uniquement sur un code de sortie différent de zéro?

OrangeTux
la source
1
Seriez-vous prêt à utiliser l'interface + python ou l'une des autres interfaces de langage?
joeytwiddle
1
@joeytwiddle Yes
OrangeTux

Réponses:

52

:silent exec "!command"

Notez que votre session vim sera toujours occupée pendant l'exécution de votre commande. Cela est dû à la nature synchrone de Vim. Vous pouvez toujours revenir à votre shell en appuyant sur CTRL + z (pour envoyer Vim en arrière-plan), puis reprenez vim avec la commande fgcomme d'habitude.

Pour faire les choses de manière asynchrone, jetez un coup d'œil au plugin de Tim Pope, vim-dispatch, ou au projet NeoVim, qui prend en charge de manière native l'exécution de commandes asynchrones, que vous pouvez facilement utiliser à l'aide du plugin NeoMake. La dernière version de Vim prend également en charge les tâches asynchrones.

Voir : h: silencieux

OliverUv
la source
5
C’est aussi la première chose que j’ai essayée quand j’ai vu la question :-) Mais si vous utilisez une commande qui sort vers stdout (ou stderr), elle "obstruera" votre fenêtre principale de Vim (essayez :silent !ls) ... ne donnez pas une sortie correcte sur un code de sortie différent de 0 ... J'ai donc peur que ce soit un peu plus :silent
compliqué
Oh pardon. Ajout de quelques notes sur l'exécution asynchrone pendant que vous écriviez votre commentaire. Je ne sais pas comment résoudre le problème du statut de sortie. Vous voudrez peut-être essayer #vim sur Freenode.
OliverUv
Je tiens également à noter que :silent exec "!ls"ni même :silent !lsafficher aucune sortie du tout sur ma version de Vim, 7.4 avec les correctifs 1-213.
OliverUv
3
Hm, voici ce que je reçois après :silent !ls: i.stack.imgur.com/1XvS6.png ... Je dois presser ^Lpour le fixer à nouveau ...
Martin Tournoij
vim-dispatch apparaît certainement comme une solution au problème posé.
joeytwiddle
30

Pour exécuter une commande sans déclencher le message Enter, procédez comme suit:

Appuyez sur ENTREE ou tapez commande pour continuer

essayez l'exemple simple suivant:

:silent !echo Hello

Appuyez ensuite sur Ctrl+ L(ou :redraw!) pour actualiser l’écran lorsque vous revenez à Vim.

Pour éviter d'avoir à actualiser, vous pouvez définir votre propre commande personnalisée, telle que:

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

Maintenant, vous pouvez utiliser la nouvelle commande Vim pour exécuter une commande shell (remarque: sans ! Et avec S majuscule ):

:Silent ps

Source: Éviter les invites "Appuyez sur Entrée pour continuer" sur le site Vim Wikia

Kenorb
la source
1
Comment obtenez-vous une notification sur un code de sortie non nul?
Martin Tournoij
1
Ce n'est pas une solution au code de sortie non nul, mais cette commande Silent permet d'ajouter des commandes similaires :Silent ctags -R . > /dev/null &à celles exécutées en arrière-plan. L'envoi de stdout à / dev / null empêche la sortie d'apparaître dans le tampon vim.
Ftvs
10

Une solution rapide et sale (dans Vimscript pur)

Cela peut démarrer un processus en arrière-plan:

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

Mais Vim a besoin d'un moyen de savoir quand le processus est terminé et comment utiliser un fichier marqueur:

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

Maintenant, nous pouvons demander à Vim de vérifier de temps en temps si le processus est terminé:

: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

Hé, ce n'est pas joli, mais ça marche en quelque sorte!

Désavantages

Malheureusement, la commande autocmd ci-dessus ne se déclenchera que lorsque vous aurez déplacé le curseur. Et si vous souhaitez exécuter plusieurs processus d'arrière-plan à la fois, vous devez ajouter des ID uniques à ces fichiers et au nom du groupe autocmd.

Donc, si vous pouvez gérer une dépendance supplémentaire, vous feriez peut-être mieux d'utiliser une solution éprouvée telle que le plugin vim-dispatch mentionné dans une autre réponse.

Affichage de la sortie

Si vous voulez voir la sortie du processus, plutôt que juste le code de sortie , remplacez le final echoci-dessus par:

silent botright pedit /tmp/output

Cela ouvrirait la fenêtre de prévisualisation. Pour utiliser la liste d'erreurs de correction rapide à la place:

silent cget /tmp/output | copen

La liste des solutions rapides vous permet de naviguer facilement en utilisant les erreurs :cnext. Cependant, copendéplace votre curseur dans la fenêtre de correction rapide lors de son ouverture, ce qui sera probablement surprenant / ennuyeux.

(Pour contourner ce problème, ouvrez la fenêtre QF :copenlors du démarrage initial du processus. Vous n'aurez donc pas besoin de l'appeler à la fin.)

Joeytwiddle
la source
2
C’est la seule réponse ici qui réponde effectivement à la question: "exécute une commande en arrière-plan qui ne m’informe que sur un code de sortie non nul" ... Je ne sais pas pourquoi les autres réponses ont même des votes à la hausse ...
Martin Tournoij
2
Je crois qu'ils ont des votes positifs, car ils répondent parfaitement au titre de la question, ce qui attire les visiteurs sur cette page. "Comment exécuter des commandes shell en silence?" Un phénomène de SE commun! Les visiteurs sont reconnaissants, mais peut-être pas disciplinés.
joeytwiddle
Dans un monde idéal, nous aurions probablement deux questions distinctes: "Comment exécuter des commandes shell silencieusement?" et "Comment exécuter des commandes shell en arrière-plan?" afin que les googleurs puissent trouver ce qu’ils cherchent.
joeytwiddle
6

Exécuter une commande en arrière-plan

J'exécutais une commande qui bloquait un peu, et je ne me souciais pas vraiment de la sortie. Ceci peut être pris en charge en démarrant le processus attaché non pas au terminal / émulateur mais au système et en redirigeant toute la sortie vers / dev / null. Par exemple:

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

le l' (...&)exécute en arrière-plan et > /dev/nullredirige toutes les sorties vers /dev/null(rien).

La parenthèse emprisonne la sortie dans un sous-shell (ou quelque chose du genre), mais elle a pour effet secondaire de ne pas être attachée au shell actuel (ce n'est pas grave).

Exécution silencieuse d'une commande en arrière-plan avec mappage

Je viens de réaliser que si vous êtes , en fait, la cartographie, vous pouvez faire quelque chose comme

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

Ce qui précède n’affiche rien dans la barre de commande et n’affiche rien dans le terminal en dehors de vim. Il sera mappé sur <leader> e, ce qui est \ epar défaut.

Exécuter une commande, capturer sa sortie, afficher son contenu

(Une autre édition - et peut-être la plus ordonnée). Celui-ci affichera le résultat d'une commande si vous le souhaitez:


DANS UNE NOUVELLE ONGLET:

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

À L'INTÉRIEUR D'UN SPLIT VERTICAL:

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

À L'INTÉRIEUR D'UN SPLIT HORIZONTAL:

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

... fais ce que tu veux

dylnmc
la source
5

Si vous ne vous souciez pas du code de sortie, vous pouvez aller avec ceci:

:call system('/usr/bin/zathura using-docker.pdf &')
carnet de notes
la source
1
Si vous vous souciez du code de sortie, la v:shell_errorvariable vous le dira
Jerome Dalbert
4

Vous ne savez pas si cela répond à vos besoins (ne gère pas les processus d'arrière-plan comme dans foo & ; vous ne savez pas si c'est ce que vous entendez par «en arrière-plan» ), mais une commande personnalisée pourrait être utilisée comme suit:

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

Puis utilisez comme par exemple:

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

Si vous n'avez pas besoin / ne voulez pas du message d'erreur, une simple system()pourrait suffire, puis vérifiez v:shell_erroret signalez par exemple echo Error!.

De l' aide 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
la source
Ce n'est pas vraiment en cours d'exécution en arrière-plan; vous devez encore attendre la fin.
Martin Tournoij
@Carpetsmoker: Oui, d'où mon commentaire «… ne gère pas les processus en arrière-plan comme dans foo &…» . Dans l'ensemble du texte Q, je l'ai interprété comme étant l'un ou l'autre. Soit “Exécuter en tant que commande externe AKA en arrière-plan” ou “Exécuter en tant que commande externe _et_ en tant que processus en arrière-plan.” . J'ai répondu principalement en fonction du titre de Q etc. - et ajouté un commentaire sur «Pas sûr…» - et laissé à OP le soin de préciser si l'un ou l'autre.
Runium le
3

Vim 8 a introduit le support aux emplois. On peut exécuter une commande externe en arrière-plan sans s'appuyer sur des plugins. Par exemple, pour exécuter un serveur de démarque ( markserv ) à l’emplacement actuel et ne pas bloquer la session vim:

:call job_start('markserv .')

Cela démarre le processus de serveur de notes en tant que sous-processus du processus vim actuel. Vous pouvez vérifier cela avec pstree.

Voir job_start

KFL
la source
2

J'utilise le code suivant:

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

Maintenant, vous pouvez taper le texte suivant

:Silent !your_command

Cela ressemble presque à la silent !commande intégrée de Vim , à l'exception de la capitale S. Il permet même une option !pour le rendre encore plus similaire. L'appel à system()rend la commande shell vraiment silencieuse: aucun écran ne clignote ni aucun redessin.

(et si vous avez besoin d'un code d'état, vous pouvez vérifier la v:shell_errorvariable, voir l'aide pour plus d'informations)

Jérôme Dalbert
la source
1

Le plug- in AsyncRun, s'il est conçu à cet effet, vous permet d'exécuter des commandes de shell en arrière-plan dans Vim8 / NeoVim et d'afficher le résultat dans la fenêtre du correctif rapide.

Juste en remplacement de la !commande:

:AsyncRun ls -la /

Vous pouvez également masquer complètement la sortie dans la fenêtre du correctif:

:AsyncRun -mode=3 ls -la /

Une fois le travail terminé, AsyncRun vous en informera en passant une option -post:

:AsyncRun -post=some_vim_script   ls -la /

Le script vim défini par -postsera exécuté une fois le travail terminé.

skywind3000
la source