l0b0
l0b0

Reputation: 58928

Pipe status after command substitution

I'd like to send the result of a series of commands to a variable:

variable=$(a | few | commands)

However, the command substitution resets PIPESTATUS, so I can't inspect where it went wrong after the fact. One solution would be to use mktemp and put the result there temporarily:

variable_file=$(mktemp) || exit 1
a | few | commands > $variable_file
exit_codes="${PIPESTATUS[*]}"
variable=$(<$variable_file)

Is there a more elegant solution?

Upvotes: 12

Views: 3408

Answers (2)

Jon
Jon

Reputation: 1142

Building on ephemient's answer, if we need the output of the piped commands stored without them being mixed in with the pipestatus codes, but we don't really care what the exit codes themselves are (just that they all succeeded), we can do:

variable=$(a | few | commands; [[ ${PIPESTATUS[*]} == "0 0 0" ]])

This will check on the status of all the piped command status in the above example and if its exit code is not 0, will set $? to 1 (false)

If you want to exit with a different code instead of 1, you could capture the contents of PIPESTATUS[#], e.g. r_code=${PIPESTATUS[2]}, and then use (exit ${r_code[2]}) instead of false.

Below captures all the codes of PIPESTATUS, ensures they're all 0, and if not, sets the exit code to be the $? value of commands:

declare -a r_code 
variable=$(a | few | commands
           r_code=(${PIPESTATUS[@]})
           [[ ${r_code[*]} == "0 0 0" ]] || (exit ${r_code[2]})
)

echo ${?}         # echoes the exit code of `commands`
echo ${variable}  # echoes only the output of a | few | commands

Upvotes: 1

ephemient
ephemient

Reputation: 204956

Kinda hacky but I think you could fudge it like this.

variable=$(a | few | commands; echo ": ${PIPESTATUS[*]}")
PIPESTATUS=(${variable##*: })
variable=${variable%:*}
variable=${variable%$'\n'}

Upvotes: 8

Related Questions