Empêcher les EOF automatiques sur un canal nommé et envoyer un EOF quand je le veux

12

J'ai un programme qui se ferme automatiquement lors de la lecture d'un EOF dans un flux donné (dans le cas suivant, stdin).
Maintenant, je veux créer un script shell, qui crée un canal nommé et y connecte le stdin du programme. Ensuite, le script écrit plusieurs fois dans le tuyau à l' aide de echoet cat(et d'autres outils qui génèrent automatiquement un EOF à leur sortie). Le problème auquel je suis confronté est que, lorsque le premier echoest terminé, il envoie un EOF au tuyau et fait quitter le programme. Si j'utilise quelque chose comme tail -fça, je ne peux pas envoyer d'EOF lorsque j'ai l'intention de quitter le programme. Je recherche une solution équilibrée mais en vain.
J'ai déjà trouvé à la fois comment empêcher les EOF et comment envoyer manuellement un EOF mais je ne peux pas les combiner. Y a-t-il un indice?

#!/bin/sh
mkfifo P
program < P & : # Run in background
# < P tail -n +1 -f | program
echo some stuff > P # Prevent EOF?
cat more_stuff.txt > P # Prevent EOF?
send_eof > P # How can I do this?
# fg
iBug
la source

Réponses:

13

Comme d'autres l'ont indiqué, le lecteur d'une pipe reçoit l'EOF une fois qu'il n'y a plus d'écrivains. La solution est donc de s'assurer qu'il y a toujours un écrivain qui le tient ouvert. Cet écrivain n'a rien à envoyer, il suffit de le garder ouvert.

Puisque vous utilisez un script shell, la solution la plus simple consiste à dire au shell d'ouvrir le canal d'écriture. Et puis fermez-le lorsque vous avez terminé.

#!/bin/sh
mkfifo P
exec 3>P # open file descriptor 3 writing to the pipe
program < P
# < P tail -n +1 -f | program
echo some stuff > P
cat more_stuff.txt > P
exec 3>&- # close file descriptor 3

Notez que si vous omettez la dernière ligne, le descripteur de fichier 3 sera automatiquement fermé (et donc le lecteur recevra EOF) à la fin du script. Outre la commodité, cela offre également une sécurité de toutes sortes si le script devait se terminer en quelque sorte tôt.

Patrick
la source
2
cette exec 3>Pcause accroche bash, pourquoi?
Wang
@Wang Ça ne devrait pas. Si c'est le cas, vous ne faites probablement pas la même chose que le code POC dans la question. La seule raison pour laquelle je peux penser que cela bloquerait est si vous faites quelque chose comme exec 2>P, et que le mode de trace est activé ( set -x), dans lequel bash va écrire dans le tuyau, mais il n'y a pas de lecteur, donc il bloque l'attente quelque chose à lire.
Patrick
1
@Wang @Patrick Effectivement se exec 3>Pbloque en bash sur ma machine aussi. C'est parce qu'il n'y a pas de lecture de processus P. Ainsi, la solution était de permuter les lignes exec 3>Pet program < P &(en ajoutant l'esperluette pour que le programme s'exécute en arrière-plan).
macieksk
2

Une pipe reçoit EOF lorsque le dernier écrivain s'en va. Pour éviter cela, assurez-vous qu'il y a toujours un écrivain (un processus qui a le canal ouvert pour l'écriture, mais qui n'écrit en fait rien). Pour envoyer l'EOF, faites disparaître cet écrivain de réserve.

mkfifo P
while sleep 1; do :; done >P &
P_writer_pid=$!
send_eof_to_P () {
  kill $P_writer_pid
}
Gilles 'SO- arrête d'être méchant'
la source
0

Il n'y a aucun moyen pour le programme de faire la distinction entre un EOF qui signifie "il est temps de quitter" et un EOF qui signifie "un écrivain est terminé, mais il peut y avoir plus de contribution de quelqu'un d'autre".

Si vous avez la possibilité de modifier le comportement de votre programme, alors faites la lecture dans une boucle infinie (une itération dure jusqu'à EOF) et envoyez-lui une chaîne de commande spécifique qui signifie "le temps de quitter". L'envoi de cette chaîne serait la tâche de la send_eofcommande dans votre question.

Une autre option:

( echo some stuff; cat more_stuff.txt ) >P

ou

{ echo some stuff; cat more_stuff.txt; } >P
Kusalananda
la source