web-dev-qa-db-fra.com

Comment éviter la fermeture d'écho FIFO canaux nommés? - Comportement drôle des FIFO Unix

Je veux sortir des données sur un tube et demander à l'autre processus de faire quelque chose ligne par ligne. Voici un exemple de jouet:

mkfifo pipe
cat pipe&
cat >pipe

Maintenant, je peux entrer ce que je veux, et après avoir appuyé sur Entrée, je vois immédiatement la même ligne. Mais si vous remplacez le deuxième tube par echo:

mkfifo pipe
cat pipe&
echo "some data" >pipe

Le tuyau se ferme après echo et cat pipe& se termine pour que je ne puisse plus transmettre de données via le canal. Existe-t-il un moyen d'éviter de fermer le canal et le processus qui reçoit les données, afin que je puisse passer de nombreuses lignes de données à travers le canal à partir d'un script bash et les faire traiter à leur arrivée?

50
user1084871

Lorsqu'un FIFO est ouvert pour la lecture, il bloque le processus d'appel (normalement). Lorsqu'un processus ouvre le FIFO pour l'écriture, le lecteur est débloqué). Lorsque l'écrivain ferme le FIFO, le processus de lecture obtient EOF (0 octet à lire), et il n'y a rien de plus qui puisse être fait sauf fermer le FIFO et rouvrir. Ainsi, vous devez utiliser une boucle:

mkfifo pipe
(while cat pipe; do : Nothing; done &)
echo "some data" > pipe
echo "more data" > pipe

Une alternative est de garder un processus avec le FIFO ouvert.

mkfifo pipe
sleep 10000 > pipe &
cat pipe &
echo "some data" > pipe
echo "more data" > pipe
47
Jonathan Leffler

Mettez toutes les instructions que vous souhaitez sortir dans le fifo dans le même sous-shell:

# Create pipe and start reader.
mkfifo pipe
cat pipe &
# Write to pipe.
(
  echo one
  echo two
) >pipe

Si vous avez un peu plus de complexité, vous pouvez ouvrir le canal d'écriture:

# Create pipe and start reader.
mkfifo pipe
cat pipe &
# Open pipe for writing.
exec 3>pipe
echo one >&3
echo two >&3
# Close pipe.
exec 3>&-
52
Mark Edgar

Vous pouvez résoudre ce problème très facilement en ouvrant le côté lecture du tuyau en mode lecture-écriture. Le lecteur ne reçoit un EOF que lorsque le dernier scripteur se ferme. L'ouvrir en lecture-écriture garantit donc qu'il y a toujours au moins un scripteur.

Modifiez donc votre deuxième exemple en:

mkfifo pipe
cat <>pipe &
echo "some data" >pipe
21
Patrick

Honnêtement, la meilleure façon de le faire fonctionner était d'utiliser socat, qui connecte essentiellement deux sockets.

mkfifo foo
socat $PWD/foo /dev/tty

Maintenant, dans un nouveau terme, vous pouvez:

echo "I am in your term!" > foo
# also (surprisingly) this works
clear > foo

L'inconvénient est que vous avez besoin de socat, ce qui n'est pas un usage de base pour tout le monde. Le côté positif est que je ne trouve pas quelque chose qui ne fonctionne pas ... Je peux imprimer des couleurs, tee sur le fifo, effacer l'écran, etc. C'est comme si vous asservissiez le tout Terminal.

2
Jordan

J'ai amélioré la deuxième version de la réponse de Jonathan Leffler pour soutenir la fermeture du tuyau:

dir=`mktemp -d /tmp/temp.XXX`
keep_pipe_open=$dir/keep_pipe_open
pipe=$dir/pipe

mkfifo $pipe
touch $keep_pipe_open

# Read from pipe:
cat < $pipe &

# Keep the pipe open:
while [ -f $keep_pipe_open ]; do sleep 1; done > $pipe &

# Write to pipe:
for i in {1..10}; do
  echo $i > $pipe
done

# close the pipe:
rm $keep_pipe_open
wait

rm -rf $dir
2
silyevsk

Comme alternative aux autres solutions ici, vous pouvez appeler cat dans une boucle comme entrée de votre commande:

mkfifo pipe
(while true ; do cat pipe ; done) | bash

Maintenant, vous pouvez lui donner des commandes une à la fois et il ne se fermera pas:

echo "'echo hi'" > pipe
echo "'echo bye'" > pipe

Vous devrez bien sûr tuer le processus quand vous le voudrez. Je pense que c'est la solution la plus pratique car elle vous permet de spécifier le comportement de non-sortie lorsque vous créez le processus.

1
Adam Dingle