bash
В версии 4 есть coproc
команда, которая позволяет делать это в чистом виде bash
без именованных каналов:
coproc cmd1
eval "exec cmd2 <&${COPROC[0]} >&${COPROC[1]}"
Некоторые другие оболочки также могут делать то же самое coproc
.
Ниже приведен более подробный ответ, но он объединяет три команды, а не две, что делает лишь немного более интересным.
Если вы счастливы также использовать, cat
а stdbuf
затем сделать конструкцию проще для понимания.
Версия использования bash
с cat
и stdbuf
, легко понять:
# 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}"
Обратите внимание, нужно использовать eval, потому что расширение переменной в <& $ var недопустимо в моей версии bash 4.2.25.
Версия с использованием pure bash
: разбить на две части, запустить первый конвейер в режиме coproc, а затем перекусить вторую часть (либо одну команду, либо конвейер), заново подключив его к первой:
coproc {
cmd 1 | cmd2
}
endcmd="exec cmd3 <&${COPROC[0]} >&${COPROC[1]}"
eval "${endcmd}"
Подтверждение концепции:
файл ./prog
, просто фиктивная прога для потребления, пометки и повторной печати строк. Использование подоболочек, чтобы избежать проблем с буферизацией, может быть, излишним, но это не главное
#!/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
файл ./start_cat
Это версия, использующая bash
, cat
иstdbuf
#!/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}"
или файл ./start_part
. Это версия, использующая bash
только чистый . Для демонстрационных целей я все еще использую, stdbuf
потому что ваша настоящая прога должна была бы в любом случае иметь дело с внутренней буферизацией, чтобы избежать блокировки из-за буферизации.
#!/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}"
Выход:
> ~/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
Это делает это.