This example was submitted (originally in BashFAQ/002) for capturing stdout / stderr to two separate variables.
# Execute a command and store stdout and stderr in two separate variables using only FD redirections # # $1: variable name to store stdout command output # $2: variable name to store stderr command output # $3: variable name to store command return code # $4: command to execute # $5: first command argument # ... # $n: last command argument execute_and_store_std_out_err() { local p_stdout="$1" local p_stderr="$2" local p_return_code="$3" shift 3 || return [ "${p_stdout}" != "stdout" ] || return [ "${p_stdout}" != "" ] || return [ "${p_stderr}" != "stderr" ] || return [ "${p_stderr}" != "" ] || return [ "${p_return_code}" != "return_code" ] || return [ "${p_return_code}" != "" ] || return source <( { { { stdout="$("$@")" } 2>&1 return_code="$?" declare -p stdout return_code >&2 } | { stderr="$(cat)" declare -p stderr >&2 } } 2>&1 ) eval "${p_stdout}"'="${stdout}"' || return eval "${p_stderr}"'="${stderr}"' || return eval "${p_return_code}"'="${return_code}"' || return }
Sample session:
user@domain:~$ unset out err ret ; declare -p out err ret ; execute_and_store_std_out_err out err ret foo ; declare -p out err ret bash: declare: out: not found bash: declare: err: not found bash: declare: ret: not found declare -- out="message on stdout" declare -- err="message on stderr" declare -- ret="42" user@domain:~$
This is my attempt at cleaning it up, which is difficult because it involves indirection, which is hard to make presentable as a good quality example.
# required wrapper for Bash unset2() { command unset "$@"; } # Execute a command and store stdout and stderr in two separate variables using only FD redirections # # $1: variable name to store stdout command output # $2: variable name to store stderr command output # $3: variable name to store command return code # $4: command to execute # $5: first command argument # ... # $n: last command argument function execute_and_store_std_out_err { typeset p_stdout=$1 typeset p_stderr=$2 typeset p_return_code=$3 [[ $p_stdout == !(stdout|) || $p_stderr == !(stderr|) || $p_return_code == !(return_code|) ]] || return shift 3 source <( { { { stdout=$("$@") } 2>&1 return_code=$? typeset -p stdout return_code >&3 } | { stderr=$(</dev/fd/0) typeset -p stderr >&3 } } 3>&1 ) eval "unset2 -v p_{std{out,err},return_code}; ${p_stdout}=\$stdout ${p_stderr}=\$stderr ${p_return_code}=\$return_code" }