🎓 A1Learner
🔍
0 of 45 lessons completed

Bash Coprocesses

A Bash coprocess is a background command that stays alive to exchange messages with the script. The script writes to its input and reads from its output while both keep running.

One message there and back

The script sends one line into the coprocess and reads one line back. The reply proves the message travelled both ways.

coproc { while IFS= read -r line; do echo "got: ${line^^}"; done; }
printf 'hello\n' >&"${COPROC[1]}"
IFS= read -r reply <&"${COPROC[0]}"
echo "$reply"
exec {COPROC[1]}>&-
wait
echo "worker done"

Two exchanges with one coprocess

The same coprocess answers twice, once per message. Both sums prove the coprocess stayed alive between exchanges.

coproc { while read -r a b; do echo $((a + b)); done; }
printf '3 4\n' >&"${COPROC[1]}"
IFS= read -r sum <&"${COPROC[0]}"
echo "first sum: $sum"
printf '10 5\n' >&"${COPROC[1]}"
IFS= read -r sum <&"${COPROC[0]}"
echo "second sum: $sum"
exec {COPROC[1]}>&-
wait

A coprocess that exits on its own

This coprocess sends exactly three lines and then exits. The script reads exactly three replies, so nothing waits forever.

coproc { printf 'one\ntwo\nthree\n'; }
for i in 1 2 3; do
  IFS= read -r word <&"${COPROC[0]}"
  echo "word: $word"
done
wait
echo "worker done"

Example

coproc { count=0; while IFS= read -r line; do count=$((count + 1)); echo "count: $count"; done; }
for word in apple banana cherry; do
  printf '%s\n' "$word" >&"${COPROC[1]}"
  IFS= read -r reply <&"${COPROC[0]}"
  echo "$reply"
done
exec {COPROC[1]}>&-
wait
echo "counter done"

Key points

  • coproc: the coproc keyword starts a coprocess and records its file descriptors in the COPROC array.
  • COPROC[0]: the script reads the coprocess output from this descriptor.
  • COPROC[1]: the script writes the coprocess input to this descriptor.
  • Bounded reads: read exactly the replies you expect, so the script never waits on a silent coprocess.
  • Clean ending: close the input descriptor so the coprocess sees end of input, then wait for it.
  • Default array: a second unnamed coproc replaces the COPROC array, so finish the first coprocess first.

In this example, a coprocess keeps a running count across messages. Each loop turn sends one word and reads back the new count. The counts rise across turns, which proves the coprocess kept its state. Closing the input ends its read loop, and wait collects it.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

After sending lines to a coprocess, why must the script close the coprocess input before wait?