¿Cómo ejecutar comandos de shell en silencio?


70

:!<command>se puede usar para ejecutar un comando en el shell. Pero esto "se hace cargo" de mi terminal y lo llena con stdoutese comando en particular.

¿Cómo ejecuto un comando en segundo plano que solo me notifica en un código de salida distinto de cero?


1
¿Estaría dispuesto a usar la interfaz + python o una de las otras interfaces de idiomas?
joeytwiddle

1
@joeytwiddle Sí
OrangeTux

Respuestas:


52

:silent exec "!command"

Tenga en cuenta que su sesión vim seguirá ocupada mientras se ejecuta su comando. Esto se debe a la naturaleza sincrónica de Vim. Todavía puede volver a su shell presionando CTRL + z (para enviar Vim a segundo plano) y luego reanudar vim con el comando fgcomo de costumbre.

Para hacer las cosas de forma asincrónica, eche un vistazo al complemento vim-dispatch de Tim Pope o al proyecto NeoVim, que tiene soporte nativo para la ejecución de comandos asincrónicos, que puede aprovechar fácilmente utilizando el complemento NeoMake. La última versión de Vim también tiene soporte para tareas asincrónicas.

Ver : h: silencioso


55
Esto también es lo primero que probé cuando vi la pregunta :-) Pero si usa un comando que sale a stdout (o stderr), "golpeará" su ventana principal de Vim (intente :silent !ls) ... También no No dé la salida adecuada en un código de salida que no sea 0 ... Así que me temo que es un poco más complicado que simplemente usar :silent...
Martin Tournoij

Oh, lo siento. Se agregaron algunas notas sobre la ejecución asincrónica mientras escribía su comentario. No sé cómo abordar el problema del estado de salida. Es posible que desee probar en #vim en Freenode.
OliverUv

También quiero señalar que :silent exec "!ls"ni :silent !lsmostrar ningún resultado en absoluto en mi versión de Vim, 7.4 con parches 1-213.
OliverUv

3
Hm, esto es lo que obtengo después :silent !ls: i.stack.imgur.com/1XvS6.png ... Necesito presionar ^Lpara arreglarlo nuevamente ...
Martin Tournoij

vim-dispatch ciertamente parece una solución al problema dado.
joeytwiddle

30

Para ejecutar un comando sin activar el mensaje Enter, como:

Presione ENTRAR o escriba comando para continuar

pruebe el siguiente ejemplo simple:

:silent !echo Hello

Luego presione Ctrl+ L(o :redraw!) para actualizar la pantalla cuando regrese a Vim.

Para evitar la necesidad de actualizar, puede definir su propio comando personalizado, como:

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

Ahora, puede usar el nuevo comando Vim para ejecutar un comando de shell (nota: ¡ sin ! Y con S mayúscula ):

:Silent ps

Fuente: Evitar las indicaciones "Hit ENTER para continuar" en el sitio de Vim Wikia


1
¿Cómo se le notifica sobre un código de salida distinto de cero?
Martin Tournoij

1
No es una solución para el código de salida distinto de cero, pero para agregar a esto, este comando silencioso permite que los comandos se :Silent ctags -R . > /dev/null &ejecuten en segundo plano. Enviar stdout a / dev / null evita que la salida aparezca en el búfer vim.
ftvs

10

Una solución rápida y sucia (en puro Vimscript)

Esto puede iniciar un proceso en segundo plano:

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

Pero Vim necesita una forma de averiguar cuándo se completó el proceso y cómo, así que usemos un archivo marcador:

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

Ahora podemos pedirle a Vim que verifique cada cierto tiempo si el proceso se ha completado:

: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

Oye, no es bonito, ¡pero funciona!

Desventajas

Lamentablemente, el autocmd anterior no se activará hasta que mueva el cursor. Y si desea ejecutar más de un proceso en segundo plano a la vez, necesitará agregar ID únicos a esos archivos y al nombre del grupo autocmd.

Entonces, si puede manejar una dependencia adicional, es mejor que use una solución probada como el complemento vim-dispatch mencionado en otra respuesta.

Mostrar salida

Si desea ver el resultado del proceso, en lugar de solo el código de salida , reemplace el final echoanterior con:

silent botright pedit /tmp/output

Eso abriría la ventana de vista previa. Para utilizar la lista de errores de corrección rápida en su lugar:

silent cget /tmp/output | copen

La lista de soluciones rápidas le permite navegar fácilmente a los errores usando :cnext. Sin embargo, copenmueve el cursor a la ventana de corrección rápida cuando se abre, lo que probablemente será sorprendente / molesto.

(Una solución alternativa para eso sería abrir la ventana QF :copenal iniciar el proceso inicialmente, por lo que no necesitará llamarlo al final).


2
Esta es la única respuesta aquí que realmente responde a la pregunta: "ejecutar un comando en segundo plano que solo me notifica en un código de salida distinto de cero" ... No tengo idea de por qué las otras respuestas tienen votos positivos ...
Martin Tournoij

2
Creo que tienen votos a favor porque responden perfectamente el título de la pregunta, que es lo que atrae a los visitantes a esta página. "¿Cómo ejecutar comandos de shell en silencio?" ¡Un fenómeno SE común! Los visitantes están agradecidos, pero quizás no disciplinados.
joeytwiddle

En un mundo ideal, probablemente tendríamos dos preguntas separadas: "¿Cómo ejecutar comandos de shell en silencio?" y "¿Cómo ejecutar comandos de shell en segundo plano?" para que los googlers puedan encontrar lo que están buscando.
joeytwiddle

6

Ejecutar un comando en segundo plano

Estaba ejecutando un comando que bloqueó por un momento, y realmente no me importaba la salida. Esto se puede solucionar iniciando el proceso adjunto no al terminal / emulador sino al sistema y redirigiendo toda la salida a / dev / null. Por ejemplo:

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

luego lo (...&)ejecuta en segundo plano y > /dev/nullredirige toda la salida a /dev/null(nada).

El paréntesis atrapa la salida en un subshell (o algo así), pero tiene el efecto secundario de no estar conectado al shell actual (no es gran cosa).

Ejecutar un comando silenciosamente en segundo plano con mapeo

Me he dado cuenta de que si está , de hecho, la cartografía de ella, puede hacer algo como

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

Lo anterior no mostrará nada en la barra de comandos y, además, no mostrará nada en el terminal fuera de vim. Se asignará a <leader> e, que es \ epor defecto.

Ejecutando un comando, capturando su salida, mostrando su contenido

(Otra edición, y tal vez la más bonita). Este mostrará el resultado de un comando si así lo elige:


DENTRO DE UNA NUEVA PESTAÑA:

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

DENTRO DE UN SPLIT VERTICAL:

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

DENTRO DE UN SPLIT HORIZONTAL:

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

... Haz lo que quieras


5

Si no le importa el código de salida, puede ir con esto:

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

1
Si te importa el código de salida, la v:shell_errorvariable te lo dirá
Jerome Dalbert el

4

No estoy seguro de si esto se adapta a sus necesidades (no maneja los procesos en segundo plano como en foo & - no estoy seguro si eso es lo que quiere decir con “en segundo plano” ), pero se podría usar un comando personalizado como en:

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

Luego use como por ejemplo:

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

Si no necesita / desea el mensaje de error, un simple system()podría ser suficiente, entonces verifique v:shell_errore informe por ej echo Error!.

De la ayuda 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.

Esto realmente no lo está ejecutando en segundo plano; aún debes esperar a que termine.
Martin Tournoij

@Carpetsmoker: Sí, por lo tanto mi comentario "... no maneja procesos en segundo plano como en foo &..." . Del texto Q en su conjunto lo interpreté como o bien o. Ya sea "Ejecutar como comando externo AKA en segundo plano" o "Ejecutar como comando externo _y_ como proceso en segundo plano" . Respondí principalmente en base al título de Q, etc., y agregué un comentario sobre "No estoy seguro ..." , y lo dejé a OP para aclarar si o no.
Runium

3

Vim 8 introdujo el soporte de trabajos. Uno puede ejecutar comandos externos en segundo plano sin depender de complementos. Por ejemplo, para ejecutar un servidor de rebajas ( markserv ) en la ubicación actual y no bloquear la sesión vim:

:call job_start('markserv .')

Esto inicia el proceso markserv como un subproceso del proceso vim actual. Puedes verificar esto con pstree.

Ver inicio de trabajo


2

Yo uso el siguiente 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

Ahora puede escribir lo siguiente

:Silent !your_command

Esto se parece casi al silent !comando incorporado de Vim , a excepción de la capital S. Incluso permite un opcional !para que se vea aún más similar. La llamada a system()hace que el comando de shell sea realmente silencioso: no parpadea la pantalla ni se vuelven a dibujar.

(y si alguna vez necesita un código de estado, puede verificar la v:shell_errorvariable, consulte la ayuda para obtener más información)


1

El complemento AsyncRun, si está diseñado para esto, le permite ejecutar comandos de shell en segundo plano en Vim8 / NeoVim y mostrar la salida en la ventana de revisión rápida.

Solo como reemplazo del !comando:

:AsyncRun ls -la /

También puede ocultar completamente la salida en la ventana de revisión rápida:

:AsyncRun -mode=3 ls -la /

Cuando finalice el trabajo, AsyncRun le notificará pasando una opción -post:

:AsyncRun -post=some_vim_script   ls -la /

El script vim definido por -postse ejecutará una vez que finalice el trabajo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.