How do I check if command failed... with a pipe?

So I have a script like this:

somecommad | grep --invert-match something

I'd like to be able to conditionally run a different command if somecommand fails. Here's what I tried:

somecommand | grep --invert-match something || {
    echo 'Oops'
}

But that didn't work (the grep wasn't executed). What is the proper way to do this?


Solution 1:

@steeldriver mentioned in the comments that PIPESTATUS might work. I tried it, and it worked well. Here's what I did:

somecommand | grep --invert-match something
if [ "${PIPESTATUS[0]}" != "0" ]; then
    echo 'Oops'
fi

It runs the command as before, but then I have an if statement to look at the PIPESTATUS array. I only care about the first element, so that is the one I look at. I check it it failed (if the exit code is not 0), and it it did fail, run echo 'Oops'

Solution 2:

Another way, depending on the exact behaviour needed, is to use the pipefail option:

The exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail option is enabled (see The Set Builtin). If pipefail is enabled, the pipeline's return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully.

So, if you don't care which of somecommand or grep failed, as long as one of those did fail:

set -o pipefail
if ! somecommand | grep --invert-match something; then
    echo 'Oops'
fi