Bash command substitution happens in a sub-shell, so those values exist but only inside that sub-shell. You're seeing ${PIPESTATUS[0]} reflect the $?
from the assignment (it'll be 1
if you ended the substitution with | false
).
I've changed your example to actually include some output. This will work with the original code too.
# without command substitution
echo "ABC" | false | echo "DEF"
echo "${PIPESTATUS[*]}"
echo "---"
# within command substitution
TEST="$( echo "ABC" | false | echo "DEF"; printf :%s "${PIPESTATUS[*]}" )"
declare -a PIPESTATUS2=( ${TEST##*:} ) # make array w/ content after final colon
if [[ -n "${TEST%:*}" ]]; then # if there was original output
TEST="${TEST%:*}" # remove trailing results from $TEST
TEST="${TEST%$'\n'}" # remove trailing \n like plain $(…)
else
TEST="" # no original output -> empty string
fi
echo "$TEST"
echo "${PIPESTATUS2[*]}"
Output:
DEF
141 1 0
---
DEF
141 1 0
Exit code 141 comes from the fact that false
terminated the pipeline prematurely (SIGPIPE).
This basically just appends the sub-shell's $PIPESTATUS
array to the stored value using a colon as a delimiter (any non-digit will do; I picked one I didn't have to escape) and then pulls it out of the response, populating the $PIPESTATUS2
array with those values. The removal of the final line break is another bashism. We could have used that as the delimiter, but then this would break if the original output wasn't terminated by a line break.
Simpler solution if you just want one of the exit codes:
TEST=$( echo "ABC" | false | true; exit ${PIPESTATUS[0]} )
echo $? # 141 from `echo "ABC"
Much more complex POSIX-compliant solution (there's no need for $PIPESTATUS
when you can use some dark magic): search for "Consider the pipeline" in §1d of the famous Csh Programming Considered Harmful rant and then adapt that to your POSIX-compliant needs. This will be nontrivial but highly educational if you're the kind of coder that needs to avoid bash and real languages.