News for hackers. Stuff that matters.
Re:Unix Shell Programming: The Next 50 Years [pdf] by wahern on Saturday June 05, 2021 @02:10AM (#27402649)
The simplest description I've found is at https://www.dartmouth.edu/~rc/classes/ksh/coprocesses.html, which also provides a couple of examples.

Basically, you start a coprocess using the "|&" operator:

  somecommand |&
This starts somecommand asynchronously, with stdin and stdout attached to anonymous pipes with descriptor numbers that can be referenced by a literal descriptor name, "p". So running

  cat <&p
will shuttle the output of the somecommand coprocess to stdout.

"p" always references the most recently created coprocess, so to juggle multiple coprocesses you have to dup "p" to explicit descriptors:

  exec 4>&p
  somecommand2 5<&p |&
That's not as great as having named references, but it's rare to need to independently juggle more than 1 or 2 coprocesses from the same context. You're almost always plugging them together in series or in a complex graph using subshells. In either case you're only needing to manage the same few descriptor constants. It's sort of like writing in a stack-based language, where you really only use explicit references to the top of the stack.

coprocesses provide a simple construct for cases where you would otherwise have to manually create a named pipe using mkfifo. The need for this pattern comes up surprisingly often, IME, yet because it's so messy--now you need to worry about /tmp races, cleaning things up, etc--people rarely bother, myself included. From the perspective of a shell it's a relatively simple construct to implement. The semantic benefit/cost ratio is exceptional.

This basic functionality is complemented with extension options to the read and print (non-standard) builtins: -p reads from or writes to the latest coprocess, and -u the specified descriptor number. I haven't used coprocesses much because I can't rely on them, but I don't think these are strictly needed. Maybe they were required for proper buffering in early implementations (or even current ones), where redirection to and from builtins didn't utilize the same buffered I/O context? That seems more like an implementation detail.

ksh93, bash, and some other shells provide the process substitution construct, "<()", which can provide similar capabilities to coprocesses in many cases. But I'm pretty sure anything you could do with process substitution you could do with coprocesses, making the latter the more powerful construct. coprocesses seems like the critical missing primitive from the traditional Unix shell model, and would round it out. (Of course, the sky is the limit for evolving the Unix shell model in new directions, as Oil has shown.)

ksh88 had coprocesses so I'm curious why it wasn't included in POSIX Maybe because bash lacked an implementation? Or incompatible quirks between ksh88 and ksh93 related to stdout inheritance across coprocesses?

There are no comments on this comment yet.