Terminal borked después de invocar a Vim con xargs

30

A veces he intentado invocar a Vim usando xargs, así:

find . -name '*.java' | xargs vim

... qué tipo de obras:

  1. Cuando se inicia Vim, veo brevemente la siguiente advertencia:

    Vim: Warning: Input is not from a terminal
    
  2. La edición funciona: :filesenumera correctamente todos los .javaarchivos como se esperaba.
  3. Puedo ahorrar y dejar de fumar.

Sin embargo, después de salir de Vim, mi terminal se borked:

  • Lo que escriba en el indicador de comandos de la shell no tiene eco.
  • Los retornos de carro no aparecen en absoluto, y los avances de línea solo aparecen a veces.

Esto continúa hasta que emito un reset(1)comando para reiniciar la terminal.

¿Es esto un error de Vim, o hay una explicación más satisfactoria de por qué interactúa así con el terminal? Lo he visto suceder en Vim hasta la versión 7.3 (la versión no parece importar) en Linux y varios Unices.

Soy consciente de una solución alternativa, a saber vim $(find . -name '*.java'). Serían bienvenidas otras soluciones, aunque esa no es mi pregunta principal.

200_success
fuente
Recibo tu deseo de llenar este sitio con tantas preguntas como sea posible, pero ... esa pregunta ha sido respondida docenas de veces a lo largo de los años en SO / SU y en otros lugares: xargsusa un muñeco stdinque Vim no puede usar y se rompe todo después
romainl
1
@romainl No estoy activo en esos sitios, y nunca lo he visto allí, honestamente.
200_success
Relacionado: superuser.com/questions/336016/… - tal vez alguien pueda condensar las respuestas principales a una gran respuesta.
muru
2
Gran pregunta: esto me ha sucedido varias veces. Además, no uso ningún otro sitio SO: si está relacionado con vim, me gustaría encontrarlo aquí.
craigp

Respuestas:

21

Esto sucede cuando se invoca vim y se conecta a la salida de la canalización anterior, en lugar del terminal y recibe una entrada inesperada diferente (como NUL). Lo mismo sucede cuando ejecuta: vim < /dev/nullpor lo que el resetcomando en este caso ayuda. Esto se explica bien por la gravedad en el superusuario .

Si está utilizando findpara pasar nombres de archivos para editar, no necesita xargs, solo use -exec, por ejemplo:

find . -name '*.java' -exec vim {} +

Si desea usar xargs, en Unix / macOS debe usar -oparámetros, como:

find . -name '*.java' | xargs -o vim

-oVuelva a abrir stdin como / dev / tty en el proceso secundario antes de ejecutar el comando. Esto es útil si desea que xargs ejecute una aplicación interactiva.

o:

find . -name "*.java" -type f -print0 | xargs -o -0 vim

Nota: El uso de -print0/ -0admitirá nombres de archivo con espacios en blanco.


find+ BSDxargs

En Unix / macOS puede probar la siguiente solución:

find . -name '*.java' | xargs -J% sh -c 'vim < /dev/tty $@'

También puede usar la sintaxis de sustitución de comandos, por ejemplo:

vim $(find . -name '*.java')
vim `find . -name '*.java`

Alternativamente, use GNU en parallellugar de xargsforzar la asignación de tty, por ejemplo:

find . -name '*.java' | parallel -X --tty vi

Nota: parallelen Unix / OSX no funcionará ya que tiene diferentes parámetros y no es compatible con tty.

Muchos otros comandos populares también proporcionan asignación de pseudo-tty (como -ten ssh), así que busca ayuda.

Otra sugerencia sería usar:

Relacionado:

kenorb
fuente
Mi GNU xargsno tiene -J. Esa parece ser una opción de MacOS (BSD) que no está presente en la versión de GNU.
Pausado hasta nuevo aviso.
12

Sugerencia de solución alternativa: use un búfer como navegador del sistema de archivos

Use el vim -comando para leer una lista de rutas desde stdin. Vim :help --explica esto: 1

Comience a editar un nuevo búfer, que se llena con texto que se lee desde stdin. Los comandos que normalmente se leerían desde stdin ahora se leerán desde stderr. Ejemplo:

find . -name "*.c" -print | vim -

Luego puede usar gfo Ctrl-wCtrl-fpara navegar al archivo en el mismo búfer o en una nueva ventana dividida respectivamente.

Sugerencia de solución alternativa: lista de argumentos

Otra gran manera es usar la lista de argumentos de Vim. El :argadd **/*.javacomando rellenará la lista de argumentos de Vim con todos los archivos java encontrados dentro del directorio actual de forma recursiva. Luego puede usar :nexty :prevpara moverse entre los archivos.


1 El primero -le dice a Vim que desea buscar en la ayuda una opción de línea de comando, el segundo es en realidad el indicador de línea de comando que está buscando. Lea :help help-contextpara más trucos como este.

akshay
fuente
8

Además reset, puedes probar:

stty sane

lo que también debería hacer que su terminal sea utilizable nuevamente.

Ver aquí para explicaciones. Y de alguna manera esto puede considerarse un mal comportamiento vim, al menos Neovim no tiene este problema en este momento.

ryenus
fuente
Esto no responde exactamente la pregunta, pero me ayudó, así que +1.
Restablece a Monica iamnotmaynard el
Esto responde a mi pregunta;) Aunque después de leer el OP más, creo resetque también debería hacerlo.
Sridhar Sarnobat
1

La razón es que se xargsestablece stdinen /dev/null, mientras que vimnecesita stdinser /dev/tty.


xargsSolución BSD (por ejemplo, Mac):

echo -e 'file1\nfile2' | xargs -o vim

-oestablece el stdinproceso hijo de xarg ( vimen este caso) en dev/tty.


xargsSolución GNU (por ejemplo, Linux):

GNU xargsno tiene la -oopción. En su lugar, deberá utilizar una solución alternativa más complicada. (Nota: es muy importante tener la zerocadena final , no lo olvide).

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

También puedes convertirlo en un alias:

alias vimin='xargs bash -c '\''</dev/tty vim "$@"'\'' zero'
echo -e 'file1\nfile2' | vimin

Explicación detallada de la solución GNU xargs

Desglosemos paso a paso:

echo -e 'file1\nfile2' | xargs bash -c '</dev/tty vim "$@"' zero

1. xargssimplemente agrega el stdinal final de la cadena, por xargslo que se ejecutará esto:

    bash -c '</dev/tty vim "$@"' zero file1 file2

2. El formato para bash -ces bash -c 'COMMAND_STRING' $0 $1 $2 etc.

"$@"se expande a los parámetros posicionales "$1", "$2", etc. No incluye "$ 0" porque es un parámetro especial para el nombre del script, no un parámetro posicional. Es por eso que necesitamos agregar la cadena ficticia zero(puede ser cualquier cadena) para tomar el lugar $0. De lo contrario, perderá el primer archivo.

Entonces, después de expandirse "$@", terminas con:

    bash -c '</dev/tty vim file1 file2' 

3. bash -cejecutará el COMMAND_STRING:

    </dev/tty vim file1 file2 

</dev/ttyconfigura el stdina /dev/ttypara que vimpueda funcionar en modo interactivo.

Wisbucky
fuente
+1 Esto parece ser un duplicado de la respuesta con el mayor número de votos, pero no lo es. El $0marcador de posición (aquí "cero") es la clave.
Pausado hasta nuevo aviso.
0

He encontrado que faltan todas estas respuestas. Después de luchar por el problema de xargs, la forma más simple, para mí, de hacer estas búsquedas y aperturas en un cuadro genérico es la siguiente:

vim -O `grep -lir mySearchTerm *`

No tengo problemas para usar findcon xargsy grep, pero la sintaxis me resulta molesta. Y como un programador diario que usa vimy el terminal como su IDE, y busca archivos en busca de propiedades constantemente, esto es lo que he estado usando.

Hay un momento y un lugar para find . -path ./node_modules -prune -o -name '*.js' | xargs grep -i mySearchTermcombinarlos con la apertura de archivos a través de vim y otros métodos. Para mí, eso es raramente.

Espero que esto ayude a alguien.

Gavin
fuente
2
FWIW se recomienda no usar la notación de backticks heredada y usarla $(...)en su lugar. No es tan importante en su línea de comando como en un script, pero creo que aún es mejor usar eso en sus respuestas :) (referencias aquí y aquí )
statox