web-dev-qa-db-fra.com

Coproc et nommé comportement de tuyau sous la substitution de commande

J'ai besoin de faire une fonction dans un script ZSH Shell, appelé par substitution de commande, communiquer à l'état des appels ultérieurs à la même substitution de commandement.

Quelque chose comme les variables statiques de C dans des fonctions (très grossièrement parlant).

Pour ce faire, j'ai essayé 2 approches - une utilisation des coprocesseurs et une utilisation des tuyaux nommés. L'approche de Tipes nommée, je ne peux pas me rendre au travail - ce qui est frustrant parce que je pense que cela résoudra le seul problème que j'ai avec des coprocesseurs - c'est-à-dire si j'entre dans une nouvelle coquille ZSH du terminal, je ne semble pas être capable de voir la coproc de la session ZSH parent.

J'ai créé des scripts simplifiés pour illustrer le problème ci-dessous - si vous êtes curieux de savoir ce que j'essaie de faire - il s'agit d'une nouvelle composante d'état au thème Bullet-train ZSH, qui sera appelé par la commande substituée build_promppt ( ) fonctionne ici: https://github.com/caiogondim/bullet-train.zsh/blob/d60f62c34b3D9253292EB8BE81FB46FA65D8F048/BulleT-Train.zsh-theme#L692

Script 1 - Coprocesseurs

#!/usr/bin/env zsh

coproc cat
disown
print 'Hello World!' >&p

call_me_from_cmd_subst() {
    read get_contents <&p
    print "Retrieved: $get_contents"
    print 'Hello Response!' >&p
    print 'Response Sent!'
}

# Run this first
call_me_from_cmd_subst

# Then comment out the above call
# And run this instead
#print "$(call_me_from_cmd_subst)"

# Hello Response!
read finally <&p
echo $finally

Script 2 - Tuyaux nommés

#!/usr/bin/env zsh

rm -rf /tmp/foo.bar
mkfifo /tmp/foo.bar
print 'Hello World!' > /tmp/foo.bar &

call_me_from_cmd_subst() {
    get_contents=$(cat /tmp/foo.bar)
    print "Retrieved: $get_contents"
    print 'Hello Response!' > /tmp/foo.bar &!
    print 'Response Sent!'
}

# Run this first
call_me_from_cmd_subst

# Then comment out the above call
# And run this instead
#print "$(call_me_from_cmd_subst)"

# Hello Response!
cat /tmp/foo.bar

Dans leurs formes initiales, ils produisent tous deux exactement la même sortie:

$ ./named-pipe.zsh
Retrieved: Hello World!
Response Sent!
Hello Response!

$ ./coproc.zsh
Retrieved: Hello World!
Response Sent!
Hello Response!

Maintenant, si j'allume le script Coproc pour appeler à l'aide de la substitution de commande, rien ne change:

# Run this first
#call_me_from_cmd_subst

# Then comment out the above call
# And run this instead
print "$(call_me_from_cmd_subst)"

C'est la lecture et l'écriture à la coprocession de la sous-processus créée par la substitution de commandement ne provoque aucun problème. J'étais un peu surpris par cela - mais c'est une bonne nouvelle!

Mais si je fais le même changement dans les exemples de pipi nommés, les blocs de script - sans sortie. Essayer de jauger pourquoi je l'ai couru avec zsh -x, donnant:

+named-pipe.zsh:3> rm -rf /tmp/foo.bar
+named-pipe.zsh:4> mkfifo /tmp/foo.bar
+named-pipe.zsh:15> call_me_from_cmd_subst
+call_me_from_cmd_subst:1> get_contents=+call_me_from_cmd_subst:1> cat /tmp/foo.bar
+named-pipe.zsh:5> print 'Hello World!'
+call_me_from_cmd_subst:1> get_contents='Hello World!'
+call_me_from_cmd_subst:2> print 'Retrieved: Hello World!'
+call_me_from_cmd_subst:4> print 'Response Sent!'

Il me semble que le sous-processus créé par la substitution de commande ne se termine pas tandis que la ligne suivante ne s'est pas terminée (j'ai joué avec l'utilisation de &, &!, et disown ici sans changement de résultat).

print 'Hello Response!' > /tmp/foo.bar &!

Pour démontrer cela, je peux tirer manuellement un chat pour lire la réponse:

$ cat /tmp/foo.bar
Hello Response!

Le script attend maintenant la commande finale du chat car il n'y a rien dans le tuyau à lire.


Mes questions sont:

  1. Est-il possible de construire le tuyau nommé pour se comporter exactement comme la coprocession en présence d'une substitution de commande?
  2. Pouvez-vous expliquer pourquoi une coprocès peut manifestement être lue et écrite à partir d'une sous-processus, mais si je crée manuellement un sous-(-ner) (en tapant zsh) dans la console, je ne peux plus y accéder (en fait, je peux créer Un nouveau Coproc qui fonctionnera indépendamment de son parent et de sa sortie et continuez à utiliser les parents!).
  3. Si 1 est possible, je suppose que les pipes nommées n'auront pas de telles complications que dans 2 car le tuyau nommé n'est pas lié à un processus de coque particulier?

Expliquer ce que je veux dire dans 2 et 3:

$ coproc cat
[1] 24516
$ print -p test
$ read -ep
test
$ print -p test_parent
$ zsh
$ print -p test_child
print: -p: no coprocess
$ coproc cat
[1] 28424
$ disown
$ print -p test_child
$ read -ep
test_child
$ exit
$ read -ep
test_parent

Je ne peux pas voir la coprocession de l'intérieur de l'enfant ZSH, mais je peux le voir à l'intérieur d'une sous-processus de substitution de commandement?

Enfin, j'utilise Ubuntu 18.04:

$ zsh --version
zsh 5.4.2 (x86_64-ubuntu-linux-gnu)
5
Phil

Une très légère modification de votre code fonctionne comme prévu:

#!/usr/bin/env zsh

rm -rf /tmp/foo.bar
mkfifo /tmp/foo.bar
print 'Hello World!' > /tmp/foo.bar &

call_me_from_cmd_subst() {
    get_contents=$(cat /tmp/foo.bar)
    print "Retrieved: $get_contents"
    (print 'Hello Response!' > /tmp/foo.bar &!) >/dev/null
    print 'Response Sent!'
}

# Run this first
#call_me_from_cmd_subst

# Then comment out the above call
# And run this instead
print "$(call_me_from_cmd_subst)"

# Hello Response!
cat /tmp/foo.bar

Exécution de ces rendements attendus:

Retrieved: Hello World!
Response Sent!
Hello Response!

Je ne suis pas tout à fait sûr pourquoi ceci est - il apparaît comme si ZSH croit que l'impression à la FIFO pourrait entraîner une sorte de sortie sur stdout?

3
Fox