zsh no puede ingresar al terminal al canalizar stdin y stdout con comando variable que tiene salida tty

11

Información del sistema:

macOS Sierra 10.12.6
zsh 5.4.2 (x86_64-apple-darwin16.7.0)
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)

Desplácese a los EJEMPLOS en la parte inferior si solo quiere profundizar en los ejemplos simplificados que hice.

NOTA: No soy un gran zshusuario.


Estaba mirando las fzfcombinaciones de teclas para bashy zsh.

Observe cómo ambos ejecutan un comando variable $(__fzfcmd). __fzfcmdde forma predeterminada, envía fzfa stdout y la sustitución de parámetros solo ejecuta el comando ( fzf) resultante de la salida.

Una diferencia entre el script bashy zshes que el bashcanaliza aún más la salida $(__fzfcmd)pero zshsolo la captura dentro de una matriz. Supongo que se debe a un problema en el zshmomento en que canaliza aún más la salida de fzfdonde no puede ingresar fzfy el proceso al que se canaliza fzfno obtiene ningún stdin. Su única opción es ^Zo ^C. ^CParece que el proceso de fondo por alguna razón. O tal vez sólo querían que en una matriz para que pudieran podrían ejecutar zle vi-fetch-historyen él . La bashversión hace algo de magia en la combinación de teclas con"\e^": history-expand-line

Ahora fzfno es importante. Parece que solo necesita un programa que salga ttya ser llamado por sustitución de parámetros para causar este problema. Entonces mostraré algunos ejemplos más simples.

Aquí hay algunos otros comandos que se envían al ttyque pueden causar este problema en zsh:

  • vipe (ejecutar editor en medio de una tubería)
  • 'vim -' (haga que vim lea desde stdin. similar a vipe pero no saldrá a stdout)

En los ejemplos a continuación, reemplace cada aparición de vipecon vim -si no desea realizar una instalación por separado. Solo recuerde que vim -no generará el contenido del editor en stdout como lo vipehace.

EJEMPLOS

1) echo 1 | vipe | cat            # works in both bash and zsh
2) echo 1 | $(echo vipe) | cat    # works in bash only. zsh problem with no output until I hit `^C`:
   ^C
   zsh: done                    echo 1 | 
   zsh: suspended (tty output)  $(echo vipe) | 
   zsh: interrupt               cat
   # seems like the process is backgrounded. I can still see it in jobs command

3) cat <(echo 1 | $(echo vipe))   # zsh and bash has the problem. I'm guessing because
                                  # the file isn't finished writing and cat is
                                  # blocking vipe's tty output
                                  # both their `^C` output is just:
   ^C # nothing special, as expected

4) cat < <(echo 1 | $(echo vipe)) # works in both bash and zsh
5) echo 1 | $(echo vipe) > >(cat) # works in both bash and zsh

# The following don't have and input pipe to vipe.
# Type something then send EOF with ^D
6) vipe | cat                     # works for both
7) $(echo vipe) | cat             # works for both

Ahora, me pregunto por qué 2)tiene un problema zshpero no para bashy por qué 4)y 5)soluciona el problema zsh.

Los requisitos para zshque este problema parezca ser exactamente lo que puse en el título:

  • tubo de entrada
  • comando ejecutado por sustitución de variable / parámetro que tiene ttysalida
  • tubo de salida

ACTUALIZAR

He añadido otra solución que no causa zsha tener este problema, 5). Es similar a, 4)pero en lugar de redirigir stdoutdirectamente stin, lo redirijo a un archivo que redirige al stdinuso de la sustitución de procesos.

dosis
fuente
1
Como pslo dirá la salida de , en ninguno de estos casos las conchas están congeladas o atascadas. Simplemente están esperando los procesos secundarios de la manera normal; y de hecho volverán a solicitar entradas de la manera normal una vez que esos procesos secundarios se suspendan o finalicen. El título y el cuerpo de su pregunta incluyen una premisa falsa implícita. "¿Por qué se congela mi caparazón?" es una pregunta cargada sin respuesta cuando su shell no se congela en primer lugar. Tendría una mejor pregunta para eliminar esta premisa falsa implícita.
JdeBP
Ok, puedo cambiarlo. No está realmente congelado en el sentido de que el proceso ya no puede ejecutar instrucciones en la CPU. Tienes razón en que solo está esperando. ¿Pero no está 'atascado'? Está esperando la entrada que no puedo proporcionar. ¿Cuál es un mejor término para describir esto de manera concisa? ¿No coincide con esta descripción de hang when either a computer program or system ceases to respond to inputs
dosentmatter
1
El shell no está esperando entrada. Está esperando a sus hijos. Esta pregunta sería mejor simplemente describiendo lo que sucede . No forme hipótesis e inferencias como "mi caparazón está congelado" y luego pregunte sobre las inferencias. Describa lo que sucede y pregunte sobre eso: las secuencias de entrada del terminal de caracteres especiales (que normalmente suspenderían el trabajo en primer plano, o interrumpirían o abandonarían el trabajo, o enviarían una indicación EOF al proceso de lectura desde el terminal) no tienen efecto. ¿Qué está pasando? ¿Por qué? . Esto es replicable en Debian Linux y FreeBSD / TrueOS, por cierto,
JdeBP
1
He informado del error en la lista de correo de desarrollo de zsh . Por ahora, deberías poder (echo | $(echo vipe) | cat)
evitarlo
1
Creo que el hecho de que las sustituciones del proceso se inician en segundo plano está documentado (o al menos conocido)
Stéphane Chazelas

Respuestas:

0

Creo que su problema se reduce a citar incorrectamente sus expansiones.

Citando de zsh: 14 Expansion

Un comando encerrado entre paréntesis precedido por un signo de dólar, como $(...), o citado con acentos graves, como ' ...', se reemplaza con su salida estándar, con cualquier nueva línea final eliminada. Si la sustitución no está entre comillas dobles, la salida se divide en palabras utilizando el parámetro IFS. La sustitución $(cat foo)puede ser reemplazada por la equivalente pero más rápida $(<foo). En cualquier caso, si se establece la opción GLOB_SUBST, la salida es elegible para la generación de nombre de archivo.

Tenga en cuenta que el Ejemplo # 2 en su pregunta da como resultado un eco infinito de NULL, debido a:

Si la sustitución no está entre comillas dobles, la salida se divide en palabras utilizando el parámetro IFS.

En otras palabras, el shell espera infinitamente el echo, porque el delimitador predeterminado es ESPACIO, el eco nunca se completa. Ver TLDP: Variables internas . Esto deja un tubo colgado para el catcomando.

Como presentimiento, creo que 4 y 5 funcionan debido a la redirección de salida.

eyoung100
fuente