Utility for creating a pipe from a shell script. Description =========== mkpipe make a pipe, print to stdout own pid and wait until killed by a caller. mkpipe is intended to be used from a bash script when there is a need to collect an output from more than one descriptor sepa- rately(i.e. stdout and stderr). For example, if we have some program(or another script) that output to stdout some needed data on success or output to stderr an error message on failure and these messages are in- termixed. Or we need to run many programs in a subshell and process stdout and stderr separately. After start mkpipe create a pipe with descriptor 3 connected to it read end and descriptor 4 connected to it write end. After that, it reports it own pid to stdout and wait with help of pause() call. There is mkpipe.sh which is usually located in /usr/local/lib/mkpipe/. This utility script contains 3 useful functions which simplify using of mkpipe in your scripts. See MKPIPE.SH section below. Install ======= make install command will install mkpipe binary in /usr/local/bin/ and mkpipe.sh in /usr/local/lib/mkpipe/. Options ======= -B create pipe with O_NONBLOCK -i show actual pipe size -s BYTES set pipe size to BYTES -v show program version -h show this help Using ===== Include mkpipe.sh in your script: . /usr/local/lib/mkpipe/mkpipe.sh Then use it: # data channel mkpipe 5 6 # error channel mkpipe 7 8 # debug channel mkpipe 9 10 ({ SOME SCRIPT HERE } 1>&6 2>&8 3>&10) read_all data <&5 read_all emsg <&7 read_all dbgmsg <&9 echo $data if [ "$emsg" ]; then echo "ERR: $emsg" fi echo "DBG: $dbgmsg" rmpipe 5 6 rmpipe 7 8 rmpipe 9 10 NOTE: for this style of using we must sure that pipe size is enough to hold all data for a channel! Because, a child run synchronously and only after it is finished we start to read all outputed data. See ex_logger.sh for example of creation a longlive children with pipe connection between it and a script. Examples ======== See example.sh, ex_logger.sh. Rationale ========= Sometimes there is need to execute some commands in a subshell and we need to collect an output from stdout and stderr(and sometimes from even more file descriptors; e.g. our script need to collect an output from 4 fd - 1, 2, 3 and 4 - where 1 is used for a data, 2 for errors, 3 is used for error which should be sent to a client and 4 for debug messages). Thus, simple: data=$(commands here) isn't enough for this case. We can use files as a temporary storage for an output: ({commands here} >data.out 2>err.out 3>err_client.out 4>debug.out) But with read-only fs this doesn't work. We can use the next shell function(thanks to htamas from stackexchange): # Create a pipe # $1 - read fd # $2 - write fd mkpipe() { local PID1 PID2 local FD_READ FD_WRITE FD_READ=$1 FD_WRITE=$2 tail -f /dev/null | tail -f /dev/null & PID2=$! PID1=`jobs -p %+` # Is this enough to finish with children? sleep 1s eval "exec $FD_READ</proc/$PID2/fd/0 $FD_WRITE>/proc/$PID1/fd/1" disown $PID1 kill $PID1 $PID2 } But look at sleep call. Here we have a race condition. How big it should be in order to be guaranteed to work correctly? The problem is we can't guarantee that 2 tail binaries are launched before we reach eval, due to concurrency. Here "sleep 1s" just a try to solve this problem and give a bash enough time to complete with 2 tail binaries. So, we need something that do not require a magic with timeout tweaking. How it should be done right =========================== While mkpipe can be used for long time data channel between a script and a child, for a simple output collecting it better to use some virtual FS. Like /dev/tcp. May be /dev/env or /dev/vars. I.e. script may looks like: ({commands here} >/dev/vars/data 2>/dev/vars/emsg 3>emsg_client 4>dbgmsg) after that we have variables data, emsg, emsg_client and dbgmsg. Sponsors ======== Z-Wave.Me