2011-12-07 2 views
40

Je veux sortir certaines données vers un tube et faire en sorte que l'autre processus fasse quelque chose à la ligne de données par ligne. Voici un exemple de jouet:Comment éviter les tubes nommés FIFO de fermeture d'écho? - Comportement drôle des FIFO Unix

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 second tuyau de remplacement avec echo:

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

Le tuyau se ferme après echo et cat pipe& finitions pour que je ne peux pas passer plus de données à travers le tuyau. Existe-t-il un moyen d'éviter la fermeture du canal et le processus qui reçoit les données, afin que je puisse passer plusieurs lignes de données à travers un script bash et les traiter à leur arrivée?

Répondre

38

Lorsqu'un FIFO est ouvert pour la lecture, il bloque le processus appelant (normalement). Lorsqu'un processus ouvre la FIFO pour l'écriture, le lecteur est débloqué. Lorsque l'écrivain ferme la FIFO, le processus de lecture obtient EOF (0 octets à lire), et il n'y a rien d'autre qui peut ê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 certain processus avec le FIFO ouvert.

mkfifo pipe 
sleep 10000 > pipe & 
cat pipe & 
echo "some data" > pipe 
echo "more data" > pipe 
+0

La deuxième version fait un travail exellent! Le premier ne fonctionne pas pour moi parce que je ne veux pas redémarrer le processus qui reçoit des données. – user1084871

+9

Vous pourriez être en mesure de tricher et d'avoir le 'chat' tenir le tuyau ouvert pour l'écriture en utilisant:' cat pipe 3> pipe'. La commande 'cat' n'utilisera pas le descripteur de fichier 3, mais aura le FIFO appelé pipe ouvert pour l'écriture (bien qu'il le lira sur un autre descripteur de fichier - probablement le numéro 4). –

+1

Est-ce que 'exec> 6 pipe' n'atteint pas la même chose? Assigne fondamentalement 'pipe' au descripteur de fichier 6 et le maintient ouvert pour l'écriture. Au lieu d'écrire directement sur 'pipe' vous voudrez probablement écrire dans ce descripteur en utilisant'> & 6' mais sinon il devrait le garder ouvert iirc – Haravikk

46

Mettez toutes les déclarations que vous voulez à la sortie du PEPS 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 une plus grande complexité, vous pouvez ouvrir le tuyau pour l'é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>&- 
+1

Excellente réponse, et parvient réellement à garder le tuyau ouvert à travers une séquence de commandes arbitrairement complexe. – voetsjoeba

+5

Pourriez-vous expliquer brièvement comment cela fonctionne? Surtout cette dernière ligne: 'exec 3> & -' –

+2

@NickChammas: dans le second exemple, le 'exec 3> pipe' ouvre le descripteur de fichier 3 pour écrire dans' pipe'; les deux commandes 'echo' écrivent sur pipe en vertu de la redirection de sortie; le dernier 'exec 3> & -' est la façon dont vous fermez un descripteur de fichier ouvert - descripteur 3 dans ce cas. À ce stade, le 'cat' en arrière-plan obtient un EOF et se termine. –

1

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

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

Vous pouvez maintenant alimenter les commandes une à la fois et il ne se referment pas:

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

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

1

I a amélioré la deuxième version de la réponse de Jonathan Leffler pour soutenir la fermeture du tube:

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 
14

Vous pouvez résoudre ce très facilement en ouvrant le côté lecture du tube en mode lecture-écriture. Le lecteur obtient seulement un EOF une fois que le dernier écrivain se ferme. Donc, en l'ouvrant en lecture-écriture, il y a toujours au moins un rédacteur.

changer donc votre deuxième exemple:

mkfifo pipe 
cat <>pipe & 
echo "some data" >pipe 
+1

C'est clairement la seule bonne réponse. Merci. –

+1

Avec cette méthode, je ne peux pas savoir comment fermer le tuyau, je ne peux que tuer le processus de chat qui pourrait ne pas se comporter de la même manière. Par exemple, si cat était en fait un programme awk avec un bloc END, le bloc END ne sera pas exécuté lorsqu'il recevra un SIGTERM. – pix