My solution was using fifos and the bash "coproc" builtin to get the messages and status from each command in the pipe. I'd never used fifos before. (oh boy, next time I'm using BashEclipse on Fedora). It turned into a generalized mechanism for managing any pipe command. I solved the problem, but not in 10 or 20 lines of code. more like 200 for a
robust drop-in re-usable solution (took me three days to do so).
I share my notes:
* stderr for all pipe commands goes to the fifos.
If you want to get messages from stdout, you must redirect '1>&2', like this:
PIPE_ARRAY=("cat ${IMG}.md5" "cut -f1 -d\" \" 1>&2")
You must put "2>/fifo" first. Otherwise it won\'t work. example:
cat ${IMG}.md5 | cut -f1 -d' ' 1>&2
becomes:
cat ${IMG}.md5 2>/tmp/fifo_s0 | cut -f1 -d" " 2>/tmp/fifo_s1 1>&2 ; PSA=( "${PIPESTATUS[@]}" )
* With more tha one fifo, I found that you must read each fifo in turn.
When "fifo1" gets written to, "fifo0" reads are blocked until you read "fifo1"
I did\'nt use any special tricks like "sleep", "cat", or extra file descriptors
to keep the fifos open.
* PIPESTATUS[@] must be copied to an array immediately after the pipe command returns.
_Any_ reads of PIPESTATUS[@] will erase the contents. Super volatile !
"manage_pipe()" appends '; PSA=( "${PIPESTATUS[@]}" )' to the pipe command string
for this reason. "$?" is the same as the last element of "${PIPESTATUS[@]}",
and reading it seems to destroy "${PIPESTATUS[@]}", but it's not absolutly verifed.
run_pipe_cmd() {
declare -a PIPE_ARRAY MSGS
PIPE_ARRAY=("dd if=${gDEVICE} bs=512 count=63" "md5sum -b >${gBASENAME}.md5")
manage_pipe PIPE_ARRAY[@] "MSGS" # (pass MSGS name, not the array)
}
manage_pipe () {
# input - $1 pipe cmds array, $2 msg retvar name
# output - fifo msg retvar
# create fifos, fifo name array, build cnd string from $1 (re-order redirection if needed)
# run coprocess 'coproc execute_pipe FIFO[@] "$CMDSTR"'
# call 'read_fifos FIFO[@] "M" "S"' (pass names, not arrays for $2 and $3)
# calc last_error, call _error, _errorf
# set msg retvar values (eval ${2}[${i}]='"${Msg[${i}]}"')
}
read_fifos() {
# input - $1 fifo array, $2 msg retvar name, $3 status retvar name
# output - msg, status retvars
# init local fifo_name, pipe_cmd_status, msg arrays
# do read loop until all 'quit' msgs are received
# set msg, status retvar values (i.e. eval ${3}[${i}]='"${Status[${i}]}"'
}
execute_pipe() {
# $1 fifo array, $2 cmdstr, $3 msg retvar, $4 status retvar
# init local fifo_name, pipe_cmd_status arrays
# execute command string, get pipestaus (eval "$_CMDSTR" 1>&2)
# set fifo statuses from copy of PIPESTATUS
# write 'status', 'quit' msgs to fifo
}