2011-12-07 13 views
40

Desidero inviare alcuni dati a una pipe e fare in modo che l'altro processo esegua i dati riga per riga. Ecco un esempio di giocattolo:Come evitare l'eco che chiude le pipe denominate FIFO? - Comportamento divertente dei FIFO di Unix

mkfifo pipe 
cat pipe& 
cat >pipe 

Ora posso entrare quello che voglio, e dopo aver premuto entrare ho subito vedere la stessa linea. Ma se sostituire secondo condotto con echo:

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

Il tubo chiude dopo echo e cat pipe& finiture in modo che non posso passare altri dati attraverso il tubo. C'è un modo per evitare di chiudere la pipe e il processo che riceve i dati, in modo che io possa passare molte righe di dati attraverso la pipe da uno script bash e farli processare man mano che arrivano?

risposta

38

Quando un FIFO viene aperto per la lettura, blocca il processo di chiamata (normalmente). Quando un processo apre la FIFO per la scrittura, il lettore viene sbloccato. Quando lo scrittore chiude il FIFO, il processo di lettura ottiene EOF (0 byte da leggere), e non c'è altro che si possa fare tranne chiudere il FIFO e riaprire. Pertanto, è necessario utilizzare un ciclo:

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

Un'alternativa è quella di mantenere un po 'di processo con il FIFO aperta.

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

La seconda versione fa un lavoro eccellente! Il primo non funziona per me perché non voglio riavviare il processo che riceve i dati. – user1084871

+9

Si potrebbe essere in grado di imbrogliare e fare in modo che il 'cat' tenga il tubo aperto per la scrittura usando:' cat pipe 3> pipe'. Il comando 'cat' non userà il descrittore di file 3, ma avrà la FIFO chiamata pipe aperta per la scrittura (sebbene la leggerà su un altro descrittore di file - probabilmente il numero 4). –

+1

'exec> 6 pipe' non raggiunge la stessa cosa? In pratica assegna 'pipe' al descrittore di file 6 e lo tiene aperto per la scrittura. Invece di scrivere direttamente su 'pipe', probabilmente vorrai scrivere su quel descrittore usando'> & 6', ma altrimenti dovrebbe tenerlo aperto iirc – Haravikk

46

mettere tutte le dichiarazioni che si desidera uscita al FIFO nella stessa subshell:

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

Se avete un po 'di più la complessità, è possibile aprire il tubo per la scrittura:

# 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

Risposta eccellente, e riesce effettivamente a mantenere il tubo aperto attraverso una sequenza di comandi arbitrariamente complessa. – voetsjoeba

+5

Potresti spiegare brevemente come funziona? Soprattutto quell'ultima riga: 'exec 3> & -' –

+2

@NickChammas: nel secondo esempio, 'exec 3> pipe' apre il descrittore di file 3 per scrivere su' pipe'; i due comandi 'echo' scrivono in pipe in virtù della redirezione dell'output; l'ultimo 'exec 3> & -' è come si chiude un descrittore di file aperto - descrittore 3 in questo caso. A quel punto, il 'cat' in esecuzione in background ottiene un EOF e termina. –

1

In alternativa alle altre soluzioni qui, è possibile chiamare cat in un ciclo come input per il comando:

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

Ora è possibile alimentare comanda uno alla volta e non chiuderà:

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

Dovrete uccidere il processo quando si vuole se ne è andato, ovviamente. Penso che questa sia la soluzione più comoda dal momento che ti consente di specificare il comportamento non esitante mentre crei il processo.

1

ho potenziato la seconda versione dalla risposta di Jonathan Leffler per sostenere la chiusura del tubo:

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

È possibile risolvere questo molto facilmente aprendo il lato di lettura del pipe in modalità di lettura-scrittura. Il lettore riceve un EOF solo quando l'ultimo autore si chiude. Quindi aprirlo in lettura-scrittura assicura che ci sia sempre almeno uno scrittore.

Quindi cambiare il tuo secondo esempio a:

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

Questa è chiaramente l'unica risposta giusta. Grazie. –

+1

Con questo metodo non riesco a capire come chiudere il tubo, posso solo uccidere il processo cat che potrebbe non comportarsi nello stesso modo. Ad esempio, se cat era in realtà un programma awk con un blocco END, il blocco END non verrà eseguito quando riceve un SIGTERM. – pix

Problemi correlati