xargs y vi - "La entrada no es desde un terminal"

14

Tengo alrededor de 10 php.iniarchivos en mi sistema, ubicados por todas partes, y quería examinarlos rápidamente. Intenté este comando:

locate php.ini | xargs vi

Pero vime advierte Input is not from a terminaly luego la consola comienza a ponerse realmente extraña, después de lo cual necesito presionar :q!para salir viy luego desconectarme de la sesión ssh y volver a conectar para que la consola vuelva a funcionar normalmente.

Creo que entiendo lo que está sucediendo aquí: básicamente, el comando no ha terminado cuando se viinició, por lo que tal vez el comando no haya terminado y vino piense que el terminal está en modo normal.

No tengo ni idea de cómo arreglarlo. He buscado en Google y también en unix.stackexchange.com con mala suerte.

cwd
fuente
Como nota al margen, puede ejecutar resetpara restablecer su terminal cuando se arruina (no tiene que desconectarse de la sesión ssh).
wisbucky

Respuestas:

12
vi $(locate php.ini)

Nota: esto tendrá problemas si las rutas de sus archivos tienen espacios, pero es funcionalmente equivalente a su comando.
Esta próxima versión manejará los espacios correctamente, pero es un poco más complicada (las nuevas líneas en los nombres de los archivos aún lo dividirán)

(IFS=$'\n'; vi $(locate php.ini))


Explicación:

Lo que sucede es que los programas heredan sus descriptores de archivo del proceso que los generó. xargstiene su STDIN conectado al STDOUT de locate, por lo que vino tiene idea de en qué consiste realmente el STDIN original.

Patricio
fuente
2
xargs es maravilloso, una de mis herramientas favoritas, simplemente no es adecuado para usar con programas que usan stdin para otra cosa que no sea una fuente de datos. Me gusta tu respuesta y tu explicación además de eso, así que +1 de todos modos :)
cas
@CraigSanders No me gusta porque es demasiado fácil de abusar (usar incorrectamente) y terminar rompiéndose. Nunca me he encontrado con algo que haya tenido que usar xargspara que no se pueda hacer directamente con el shell (o find). Sin embargo, puedo pensar en casos en los que sería la mejor solución. Entonces, siempre y cuando comprenda lo que xargsestá haciendo, cómo divide los argumentos, cómo ejecuta el programa, etc., y si lo está utilizando correctamente, diría que lo haga :-P
Patrick
no se puede superar para cosas como ... | awk '{print $3}' | xargs | sed -e 's/ /+/g' | bc(para sumar todos los valores del campo 3). o con sed -e 's/ /|/g'para construir una expresión regular. y sí, como cualquier herramienta, necesita saber cómo usarla y cuáles son sus limitaciones y advertencias.
cas
El vi $(...)enfoque también tiene un problema con los comodines en los shells que no sean zsh.
Stéphane Chazelas
También tenga en cuenta que con el xargsenfoque al lado del problema del espacio en blanco, los nombres de archivo con comillas simples, comillas dobles y barras inclinadas invertidas también son un problema.
Stéphane Chazelas
10

Esta pregunta ya se hizo en el foro de Superusuario.

Citando la respuesta de @grawity a esa pregunta:

Cuando invoca un programa a través de xargs, el stdin del programa (entrada estándar) apunta a / dev / null. (Dado que xargs no conoce el stdin original, hace lo siguiente mejor).

Vim espera que su stdin sea el mismo que su terminal de control, y realiza varios ioctl relacionados con el terminal en stdin directamente. Cuando se hace en / dev / null (o cualquier descriptor de archivo no tty), esos ioctls no tienen sentido y devuelven ENOTTY, que se ignora en silencio.

Esto se menciona en las páginas del manual para xarg. Desde OSX / BSD:

-o Vuelva 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.

Por lo tanto, en OSX, puede usar el siguiente comando:

find . -name "php.ini" | xargs -o vim

Si bien no hay un cambio directo en la versión de GNU, este comando funcionará. (Asegúrese de incluir la dummycadena, de lo contrario, soltará el primer archivo).

find . -name "php.ini" | xargs bash -c '</dev/tty vim "$@"' dummy

Las soluciones anteriores son cortesía de Jaime McGuigan en SuperUser . Agregándolos aquí para cualquier visitante futuro que busque este error en el sitio.

Darnir
fuente
3
+1 gracias por el -o consejo. He estado usando xargs durante años y nunca me di cuenta de que ... solo revisé la página del manual en mi sistema, eso es porque no es una función de xargs de GNU. La página de manual proporciona xargs sh -c 'emacs "$@" < /dev/tty' emacslo que, según ellos, es una alternativa más flexible y portátil (aunque es divertido que GNU prefiera la portabilidad a las características :).
cas
2

Con GNU findutilsy un shell con soporte para la sustitución de procesos (ksh, zsh, bash), puede hacer:

xargs -r0a <(locate -0 php.ini) vi

La idea es pasar la lista de archivos a través de un en -a filenamelugar de stdin. El uso -0asegura que funcione independientemente de qué caracteres o no caracteres puedan contener los nombres de archivo.

Con zsh, podrías hacer:

vi ${(0)"$(locate -0 php.ini)"}

(donde 0está el indicador de expansión de parámetros para dividir en NUL).

Sin embargo, tenga en cuenta que, al contrario de xargs -reso, todavía se ejecuta visin argumento si no se encuentra ningún archivo.

Stéphane Chazelas
fuente
0

¿Editar múltiples php.ini dentro del mismo editor?

Tratar: vim -o $(locate php.ini)

margarita
fuente
0

Este error ocurre cuando se invoca vim y está conectado a la salida de la tubería anterior, en lugar del terminal y está recibiendo 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 .

En Unix / OSX puede utilizar xargscon -olos parámetros, tales como:

locate php.ini | 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.

En Linux intente la siguiente solución:

locate php.ini | xargs -J% sh -c 'vim < /dev/tty $@'

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

locate php.ini | 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.

Alternativamente, use findpara pasar nombres de archivo para editar, por lo que no es necesario xargs, solo use -exec, por ejemplo:

find /etc -name php.ini -exec vim {} +
kenorb
fuente
0

El IFStruco de @ Patrick solo es necesario para conchas tontas como bashy zsh. fish divide la cadena en líneas nuevas de forma predeterminada.

$ vim (locate php.ini)

Y que Dios nos ayude a todos si uno de nosotros tiene un archivo con una nueva línea en su nombre. Después de 17 años usando Linux, no lo he visto ni una sola vez. Solo me molestaría en admitir nombres de archivo con nuevas líneas para los scripts que tienen que funcionar sin importar qué, pero los scripts como ese probablemente no se ejecutan vim de forma interactiva.

enigmáticoFísico
fuente
zshse divide en SPC, TAB, NL y NUL por defecto. Lo que no hace en comparación con bashes realizar una observación global del resultado para que los caracteres comodín en los nombres de archivo no sean un problema. En zsh, harías IFS=$'\0'; vi $(locate -0 php.ini)o como mostré en mi respuesta vi ${(0)"$(locate -0 php.ini)"}para un operador de división explícito. También tenga en cuenta tcsh'svi "`locate php.ini`"
Stéphane Chazelas
Aw, mierda. OK, esto funciona: $ f='not there'<ret>$ ls $f<ret>pero esto no: ls echo not there. OK parece que necesito actualizar esto un poco.
enigmáticoFísico
Sí, zsh no hace lo correcto cuando lo haces ls "$(echo test; echo other test)". Solo el pescado hace lo correcto.
enigmáticoFísico
Suponiendo que quisieras decir lo mismo sin las comillas, eso no es "correcto", eso es dividirse en líneas, es solo una opción diferente. zsh se divide en palabras de manera predeterminada (como todos los demás shells) y se le puede decir que se divida en líneas o en NUL, ya sea a través $IFSo mediante operadores explícitos ( fy 0banderas de expansión de parámetros). Para nombres de archivo arbitrarios, dividir por palabra o dividir por línea es igualmente incorrecto , debe dividir en NUL o analizar algo de codificación, lo que fishno puede hacer. En zsh, eso es IFS=$'\0'; ls -ld -- $(printf '%s\0' "$file1" "$file2")ols -ld -- ${(0)"$(printf '%s\0' "$file1" "$file2")"}
Stéphane Chazelas
Meh Dividir en nuevas líneas es lo suficientemente bueno. Como dice la respuesta, las nuevas líneas en los nombres de archivo son extremadamente raras. Literalmente nunca lo he visto suceder en 17 años. Y las líneas nuevas son separadores mucho más convenientes que los nuls.
enigmáticoFísico
0

Una forma rápida de hacerlo, en el supuesto que se puede garantizar ninguna de las rutas de archivos contienen SPC, TAB, NL, *, ?, [caracteres (también \y {...}en algunas conchas) es el uso de copias de las garrapatas (también conocido como acento grave) para ejecutar un comando antes otro comando corriendo.

P.ej

vi `find / -type f -name 'php.ini'`

El comando contenido dentro de los ticks posteriores se ejecutará primero. La salida del comando contenido se ejecuta luego mediante el comando indicado antes de los ticks de retroceso.

Por ejemplo, en la línea anterior, el find / -type f -name 'php.ini'comando se ejecutará primero, enviará la salida y luego vise ejecutará según el resultado de split + glob aplicado a esa salida.

tacotuesday
fuente
3
los back ticks se confunden demasiado fácilmente para las comillas simples. utilizar $(find ...)en su lugar.
cas
1
¿Adivina que esto también se romperá en espacios y / o nuevas líneas en los nombres de archivo?
cwd
Así es como ejecuta comandos de shell en scripts de bash. Nunca he tenido nada roto en espacios o nuevas líneas en mis scripts o cuando lo uso en un solo trazo. Sin embargo, nunca he intentado abrir varios archivos al viusar este método. Es muy posible que se rompa en nuevas líneas o espacios, dependiendo de cómo viesté leyendo y ejecutando la salida.
tacotuesday