- Article pages now have a discussion option at the bottom (moderated/captcha, but no registration needed) - recently upgraded to a newer Dokuwiki version, please report problems

The coproc keyword

:V4:

Synopsis

 coproc [NAME] command [redirections]

Description

Bash 4.0 introduced the coprocesses, a feature certainly familiar to ksh users.

coproc starts a command in the backgound setting up pipes so that you can interact with it. Optionally, the co-process can have a name NAME.

If NAME is given, the following command must be a compound command. If no NAME ist given, the command can be a simple command or a compound command.

Redirections

The redirections are normal redirections that are set after the pipe has been set up, some examples:

# redirecting stderr in the pipe
$ coproc { ls thisfiledoesntexist; read ;} 2>&1
[2] 23084
$ read -u ${COPROC[0]};printf "%s\n" "$REPLY"
ls: cannot access thisfiledoesntexist: No such file or directory

#let the output of the coprocess go to stdout
$ { coproc mycoproc { awk '{print "foo" $0;fflush()}' ;} >&3 ;} 3>&1
[2] 23092
$ echo bar >&${mycoproc[1]}
$ foobar

Here we need to save the previous file descriptor of stdout, because by the time we want to redirect the fds of the coprocess stdout has been redirected to the pipe.

Pitfalls

Avoid the command | while read subshell

The traditional KSH workaround to avoid the subshell when doing command | while read is to use a coprocess, unfortunately, it seems that bash's behaviour differ from KSH.

In KSH you would do:

ls |& #start a coprocess
while read -p file;do echo "$file";done #read its output

In bash:

#DOESN'T WORK
$ coproc ls
[1] 23232
$ while read -u ${COPROC[0]} line;do echo "$line";done
bash: read: line: invalid file descriptor specification
[1]+  Done                    coproc COPROC ls

By the time we start reading from the output of the coprocess, the file descriptor has been closed.

Buffering

In the first example, we used fflush() in the awk command, this was done on purpose, as always when you use pipes the I/O operations are buffered, let's see what happens with sed:

$ coproc sed s/^/foo/
[1] 22981
$ echo bar >&${COPROC[1]}
$ read -t 3 -u ${COPROC[0]}; (( $? >127 )) && echo "nothing read"
nothing read

Even though this example is the same as the first awk example, the read doesn't return, simply because the output is waiting in a buffer.

See this faq entry on Greg's wiki for some workarounds.

background processes

The file descriptors of the coprocesses are available to the shell where you run coproc, but they are not inherited. Here a not so meaningful illustration, suppose we want something that continuely reads the output of our coprocess and echo the result:

#NOT WORKING
$ coproc awk '{print "foo" $0;fflush()}'
[2] 23100
$ while read -u ${COPROC[0]};do echo "$REPLY";done &
[3] 23104
$ ./bash: line 243: read: 61: invalid file descriptor: Bad file
descriptor
it fails, because the descriptor is not avalaible in the subshell created by &.

A possible workaround:

#WARNING: for illustration purpose ONLY
# this is not the way to make the coprocess print its output
# to stdout, see the redirections above.
$ coproc awk '{print "foo" $0;fflush()}'
[2] 23109
$ exec 3<&${COPROC[0]}
$ while read -u 3;do echo "$REPLY";done &
[3] 23110
$ echo bar >&${COPROC[1]}
$ foobar

Here the fd 3 is inherited.

Only one coprocess at a time

The title says it all, complain to the bug-bash mailing list if you want more.

Examples

Anonymous Coprocess

First let's see an example without NAME:

$ coproc awk '{print "foo" $0;fflush()}'
[1] 22978

The command starts in the background, coproc returns immedately. 2 new files descriptors are now available via the COPROC array, We can send data to our command:

$ echo bar >&${COPROC[1]}

And then read its output:

$ read -u ${COPROC[0]};printf "%s\n" "$REPLY"
foobar

When we don't need our command anymore, we can kill it via its pid:

$ kill $COPROC_PID
$
[1]+  Terminated              coproc COPROC awk '{print "foo" $0;fflush()}'

Named Coprocess

Using a named coprocess is as simple, we just need a compound command like when defining a function:

$ coproc mycoproc { awk '{print "foo" $0;fflush()}' ;}
[1] 23058
$ echo bar >&${mycoproc[1]}
$ read -u ${mycoproc[0]};printf "%s\n" "$REPLY"
foobar
$ kill $mycoproc_PID
$
[1]+  Terminated              coproc mycoproc { awk '{print "foo" $0;fflush()}'; }

Redirecting the output of a script to a file and to the screen

#!/bin/bash
# we start tee in the background
# redirecting its output to the stdout of the script
{ coproc tee { tee logfile ;} >&3 ;} 3>&1 
# we redirect stding and stdout of the script to our coprocess
exec >&${tee[1]} 2>&1

Portability considerations

  • the coproc keyword is not specified by POSIX(R)
  • other shells might have different ways to solve the coprocess problem
  • the coproc keyword appeared in Bash version 4.0-alpha

See also

Discussion

Anthony Thyssen, 2010/06/18 04:18

Can you do a coprocess using only regular shell file descriptors? or perhaps using named pipes.

mknod ls_output ls > ls_output &

while read line; do

...

done < ls_output

Jan Schampera, 2010/06/18 10:36

Depending on your specific needs there is of course the possibility to use

  • named pipes
  • process substitution
  • command substitution
  • ...

Coprocesses as described here are just a very easy to use solution for those tasks. It's simple to setup, use and terminate a coprocess.

Anthony Thyssen, 2011/09/28 09:30

Seems to me it is about just a complex as using temporary named pipes.

Of course most difficulties with handling a coprocess is often the handling of the data streams especially if you want more than just stdin/stdout.

Since my initial feedback I have written what I would hope is the start of a guide to using co-processes. And yes it can be very much worth the effort.

http://www.ict.griffith.edu.au/anthony/info/shell/co-processes.hints Feedback welcome Anthony Thyssen A.Thyssen@griffith.edu.au

Jan Schampera, 2011/09/28 22:08

Wow... excellent knowledge collection. I crosslinked it here

Enter your comment
 
syntax/keywords/coproc.txt · Last modified: 2011/09/28 22:03 by thebonsai
GNU Free Documentation License 1.3
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0