O problema com o código de exemplo na pergunta é sutil; para gravar em um pipe nomeado, você precisa &
background o comando, senão ele irá bloquear a espera para ser lido. No entanto, quando você faz isso, os " comandos iniciados em segundo plano com &
têm sua entrada padrão redirecionada [para] /dev/null
. ", significando que /dev/null
é o que é canalizado para p
, em vez de stdin.
No Bash, a solução alternativa é simples, redirecione o stdin para o processo em segundo plano com 0<&0
. Então o exemplo funcionará corretamente:
$ echo foo | { mkfifo p; cat 0<&0 > p & cat < p; rm p; }
foo
A função compose
completa acaba assim:
compose() {
dir=$(mktemp -d) # Create a temp dir to hold the pipes
cd $dir # to avoid filename conflicts
i=0 #
mkfifo "pipe$i" # Create pipe0, the output for command $1
($1 0<&0 > "pipe$i" &) # Start $1, reading stdin and writing to pipe0
shift # Shift off $1 since it's running
for c in "$@"; do # Loop over the remaining commands
ii=$((i+1)) #
mkfifo "pipe$ii" # Create a pipe i+1, the next command's output
($c < "pipe$i" > "pipe$ii" &) # Start the next command, reading from the
i=$ii # i'th pipe and writing to the i+1'th
done #
cat "pipe$i" # Output the last pipe, executing the commands
cd - > /dev/null # Change back to the old directory
rm -rf $dir # Remove all the pipes
}