bash
wersja 4 ma coproc
polecenie, które pozwala to zrobić w czystej postaci bash
bez nazwanych potoków:
coproc cmd1
eval "exec cmd2 <&${COPROC[0]} >&${COPROC[1]}"
Niektóre inne powłoki również mogą to zrobić coproc
.
Poniżej znajduje się bardziej szczegółowa odpowiedź, ale łączy trzy polecenia zamiast dwóch, co czyni tylko trochę bardziej interesującym.
Jeśli jesteś szczęśliwy w użyciu, cat
a stdbuf
następnie skonstruowanie może być łatwiejsze do zrozumienia.
Wersja korzystająca bash
z cat
i stdbuf
łatwa do zrozumienia:
# start pipeline
coproc {
cmd1 | cmd2 | cmd3
}
# create command to reconnect STDOUT `cmd3` to STDIN of `cmd1`
endcmd="exec stdbuf -i0 -o0 /bin/cat <&${COPROC[0]} >&${COPROC[1]}"
# eval the command.
eval "${endcmd}"
Zauważ, że muszę użyć eval, ponieważ rozszerzenie zmiennych w <& $ var jest nielegalne w mojej wersji bash 4.2.25.
Wersja z użyciem pure bash
: Podziel na dwie części, uruchom pierwszy potok w ramach coproc, a następnie uruchom drugą część (jedno polecenie lub potok) ponownie łącząc go z pierwszą:
coproc {
cmd 1 | cmd2
}
endcmd="exec cmd3 <&${COPROC[0]} >&${COPROC[1]}"
eval "${endcmd}"
Dowód koncepcji:
plik ./prog
, tylko sztuczny program do konsumpcji, oznaczania i ponownego drukowania linii. Używanie podpowłoki w celu uniknięcia problemów z buforowaniem może być nadmierne, nie o to tutaj chodzi.
#!/bin/bash
let c=0
sleep 2
[ "$1" == "1" ] && ( echo start )
while : ; do
line=$( head -1 )
echo "$1:${c} ${line}" 1>&2
sleep 2
( echo "$1:${c} ${line}" )
let c++
[ $c -eq 3 ] && exit
done
file ./start_cat
To jest wersja użyciu bash
, cat
astdbuf
#!/bin/bash
echo starting first cmd>&2
coproc {
stdbuf -i0 -o0 ./prog 1 \
| stdbuf -i0 -o0 ./prog 2 \
| stdbuf -i0 -o0 ./prog 3
}
echo "Delaying remainer" 1>&2
sleep 5
cmd="exec stdbuf -i0 -o0 /bin/cat <&${COPROC[0]} >&${COPROC[1]}"
echo "Running: ${cmd}" >&2
eval "${cmd}"
lub plik ./start_part
. To jest wersja wykorzystująca bash
wyłącznie czysty . Do celów demonstracyjnych wciąż używam, stdbuf
ponieważ twój prawdziwy program musiałby i tak zająć się buforowaniem wewnętrznym, aby uniknąć blokowania z powodu buforowania.
#!/bin/bash
echo starting first cmd>&2
coproc {
stdbuf -i0 -o0 ./prog 1 \
| stdbuf -i0 -o0 ./prog 2
}
echo "Delaying remainer" 1>&2
sleep 5
cmd="exec stdbuf -i0 -o0 ./prog 3 <&${COPROC[0]} >&${COPROC[1]}"
echo "Running: ${cmd}" >&2
eval "${cmd}"
Wynik:
> ~/iolooptest$ ./start_part
starting first cmd
Delaying remainer
2:0 start
Running: exec stdbuf -i0 -o0 ./prog 3 <&63 >&60
3:0 2:0 start
1:0 3:0 2:0 start
2:1 1:0 3:0 2:0 start
3:1 2:1 1:0 3:0 2:0 start
1:1 3:1 2:1 1:0 3:0 2:0 start
2:2 1:1 3:1 2:1 1:0 3:0 2:0 start
3:2 2:2 1:1 3:1 2:1 1:0 3:0 2:0 start
1:2 3:2 2:2 1:1 3:1 2:1 1:0 3:0 2:0 start
To wystarczy