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"
}