zsh ne peut pas entrer dans le terminal lors de la tuyauterie stdin et stdout avec une commande variable qui a une sortie tty

11

Information système:

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)

Faites défiler jusqu'aux EXEMPLES en bas si vous voulez simplement vous plonger dans les exemples simplifiés que j'ai faits.

REMARQUE: je ne suis pas un grand zshutilisateur.


Je regardais les fzfraccourcis clavier pour bashet zsh.

Remarquez comment ils exécutent tous deux une commande variable $(__fzfcmd). __fzfcmdpar défaut, les sorties fzfvers stdout et la substitution de paramètres exécute simplement la commande ( fzf) résultant de la sortie.

Une différence entre le script bashet zshest que celui- bashci redirige la sortie de $(__fzfcmd)mais la zshcapture simplement dans un tableau. Ma supposition est due à un problème zshlorsque vous dirigez la sortie d' fzfoù vous ne pouvez pas entrer fzfet que le processus dirigé vers par fzfn'obtient pas de stdin. Votre seul choix est de ^Zou ^C. ^Csemble expliquer le processus pour une raison quelconque. Ou peut-être qu'ils le voulaient simplement dans un tableau afin de pouvoir fonctionner zle vi-fetch-historydessus . La bashversion fait un peu de magie dans le raccourci clavier avec"\e^": history-expand-line

Ce fzfn'est pas important maintenant . Il semble que vous ayez juste besoin d'un programme qui renvoie vers le ttypour être appelé par substitution de paramètres pour provoquer ce problème. Je vais donc montrer quelques exemples plus simples.

Voici quelques autres commandes qui sortent vers le ttyqui peuvent provoquer ce problème dans zsh:

  • vipe (exécuter l'éditeur au milieu d'un tuyau)
  • 'vim -' (faire lire vim depuis stdin. similaire à vipe mais ne pas sortir vers stdout)

Dans les exemples ci-dessous, remplacez chaque occurrence de vipepar vim -si vous ne souhaitez pas effectuer une installation distincte. N'oubliez vim -pas que le contenu de l'éditeur ne sortira pas sur stdout comme le vipefait.

EXEMPLES:

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

Maintenant, je me demande surtout pourquoi 2)a un problème pour zshmais pas pour bashet pourquoi 4)et 5)résout le problème pour zsh.

Les exigences pour zshavoir ce problème semblent être exactement ce que j'ai mis dans le titre:

  • tuyau d'entrée
  • commande exécutée par substitution de variable / paramètre qui a une ttysortie
  • tuyau de sortie

METTRE À JOUR

J'ai ajouté une autre solution qui ne provoque pas zshd'avoir ce problème, 5). C'est similaire 4)mais au lieu de rediriger stdoutdirectement vers stin, je le redirige dans un fichier qui redirige vers l' stdinutilisation de la substitution de processus.

dosentmatter
la source
1
Comme la sortie de psvous le dira, dans aucun de ces cas, les coquilles ne sont gelées ou bloquées. Ils attendent simplement les processus enfants de la manière normale; et ils reviendront en effet à l'invite de saisie de la manière normale une fois que ces processus enfants seront suspendus ou terminés. Le titre et le corps de votre question contiennent une fausse prémisse implicite. "Pourquoi ma coquille gèle-t-elle?" est une question chargée sans réponse lorsque votre shell ne gèle pas en premier lieu. Vous auriez une meilleure question pour supprimer cette fausse prémisse implicite.
JdeBP
Ok, je peux le changer. Ce n'est pas vraiment figé dans le sens où le processus n'est plus capable d'exécuter des instructions sur le CPU. Vous avez raison, c'est juste en attente. Mais n'est-ce pas «coincé»? Il attend des commentaires que je ne peux pas fournir. Quel meilleur terme pour décrire cela de manière concise? Ne correspond-elle pas à cette description du blocage when either a computer program or system ceases to respond to inputs
dosentmatter
1
Le shell n'attend pas d'entrée. Il attend ses enfants. Il serait préférable de poser cette question en décrivant simplement ce qui se passe . Ne faites pas d'hypothèses et d'inférences telles que «ma coquille est gelée» et ne posez pas de questions sur les inférences. Décrivez ce qui se passe et posez des questions à ce sujet: les séquences d'entrée de terminal de caractères spéciaux (qui suspendraient normalement le travail de premier plan, ou interrompraient ou quitteraient le travail, ou enverraient une indication EOF à la lecture de processus depuis le terminal) n'ont aucun effet. Qu'est-ce qui se passe? Pourquoi? . Ceci est réplicable sur Debian Linux et FreeBSD / TrueOS, soit dit en passant,
JdeBP
1
J'ai signalé le bogue sur la liste de diffusion du développement zsh . Pour l'instant, vous devriez pouvoir le contourner en l'enveloppant dans un sous(echo | $(echo vipe) | cat)
Stéphane Chazelas
1
Le fait que les substitutions de processus soient démarrées en arrière-plan est documenté je pense (ou du moins connu)
Stéphane Chazelas

Réponses:

0

Je crois que votre problème se résume à citer incorrectement vos extensions.

Citation de zsh: 14 Expansion

Une commande entre parenthèses précédée d'un signe dollar, comme $(...), ou citée avec des accents graves, comme ' ...', est remplacée par sa sortie standard, avec tous les retours à la ligne supprimés. Si la substitution n'est pas placée entre guillemets, la sortie est décomposée en mots à l'aide du paramètre IFS. La substitution $(cat foo)peut être remplacée par l'équivalent mais plus rapidement $(<foo). Dans les deux cas, si l'option GLOB_SUBST est définie, la sortie est éligible pour la génération de nom de fichier.

Notez que l'exemple # 2 de votre question entraîne un écho infini de NULL, en raison de:

Si la substitution n'est pas placée entre guillemets, la sortie est décomposée en mots à l'aide du paramètre IFS.

En d'autres termes, le shell attend indéfiniment le echo, car le délimiteur par défaut est SPACE, l'écho ne se termine jamais. Voir TLDP: Variables internes . Cela laisse un tuyau bloqué pour la catcommande.

En guise de intuition, je pense que 4 et 5 fonctionnent en raison de la redirection de sortie.

eyoung100
la source