1

is there a better way than the following code to prefix the output of a command but still being able to check its return value?

command > "$tmpfile" 2>&1
ret=$?
sed 's/^/  | /' "$tmpfile"
if [[ ! "$ret" -eq 0 ]]; then
    rm "$tmpfile"
    exit 1
fi
gcomte
  • 21

1 Answers1

3

With bash and a few other shells, you can use the pipefail option, which when enabled causes the exit status of a pipeline to be that of the rightmost component that failed as opposed to the rightmost component:

(set -o pipefail; cmd | sed 's/^/  | /') || exit

Another option is to redirect stdout to a process substitution instead of using a pipe:

cmd > >(sed 's/^/  | /') || exit

Though beware that sed which in that case is started in background and is not waited for, may not have finished writing by the time the next command is run.

Specific to bash, there's the $PIPESTATUS special array (zsh has a $pipestatus equivalent) which records the exit status of every component in a pipeline:

cmd | sed 's/^/  | /'; ret=${PIPESTATUS[0]}
[ "$ret" -eq 0 ] || exit "$ret"

POSIXly, you can do:

{
  ret=$(
    exec 4>&1
    {
      cmd 3>&- 4>&-
      echo "$?" >&4
    } | sed 's/^/  | /' >&3 3>&- 4>&-
  )
} 3>&1
[ "$ret" -eq 0 ] || exit "$ret"

You could use a helper function:

prefix() (
  prefix=$1; shift
  exit "$(
    exec 4>&1
    { 
      "$@" 3>&- 4>&-
      echo "$?" >&4 3>&- 4>&-
    } | PREFIX=$prefix awk '{print ENVIRON["PREFIX"] $0}' >&3 3>&- 4>&-
  )"
) 3>&1

prefix '  | ' cmd

(here switching to awk so as to be able to use arbitrary prefixes.