Capture stdout to variable and get the exit statuses of foreground pipe

Viewed 81

I want to execute a command (say ls) and sed its output, then save the stdout to a variable, like this,

OUT=$(ls | sed -n -e 's/regexp/replacement/p')

After this, if I try to access the $PIPESTATUS array, I get only 0 (which is same as $?). So, how can I get both $PIPESTATUS as well as capture the entire piped command's stdout?

Note:

  • If I only executed those piped commands and didn't capture the stdout (like ls | sed -n -e 's/regexp/replacement/p'), I get expected exit statuses in $PIPESTATUS (like 0 0)
  • If I only executed single command (without piping multiple commands) using Command Substitution and captured the stdout (like OUT=$(ls)), I get expected single exit status in $PIPESTATUS (which is same as $?)

P.S. I know, I could run the command 2 times (first to capture the stdout, second to access $PIPESTATUS without using Command Substitution), but is there a way to get both in single execution?

1 Answers

You can:

  1. Use a temporary file to pass PIPESTATUS.

    tmp=$(mktemp)
    out=$(pipeline; echo "${PIPESTATUS[@]}" > "$tmp")
    PIPESTATUS=($(<"$tmp"))  # Note: PIPESTATUS is overwritten each command...
    rm "$tmp"
    
  2. Use a temporary file to pass out.

    tmp=$(mktemp)
    pipeline > "$tmp"
    out=$(<"$tmp"))
    rm "$tmp"
    
  3. Interleave output with pipestatus. For example reserve the part from last newline character till the end for PIPESTATUS. To preserve original return status I think some temporary variables are needed:

    out=$(pipeline; tmp=("${PIPESTATUS[@]}") ret=$?; echo $'\n' "${tmp[@]}"; exit "$ret"))
    pipestatus=(${out##*$'\n'})
    out="${out%$'\n'*}"
    out="${out%%$'\n'}" # remove trailing newlines like command substitution does
    

    tested with:

    out=$(false | true | false | echo 123; echo $'\n' "${PIPESTATUS[@]}");
    pipestatus=(${out##*$'\n'});
    out="${out%$'\n'*}"; out="${out%%$'\n'}";
    echo out="$out" PIPESTATUS="${pipestatus[@]}"
    # out=123 PIPESTATUS=1 0 1 0
    

Notes:

  • Upper case variables by convention should be reserved by exported variables.
Related