13

I'm trying to execute following code:

set -euxo pipefail
yes phrase | make installer

Where Makefile uses phrase from stdin to create installer file. However, this commands ends in error code 141, which breaks my CI build. This example can be simplified to:

yes | tee >(echo yo)

From what is see here: Pipe Fail (141) when piping output into tee -- why? - this error means that pipe consumer just stopped consuming output - which is perfectly fine in my case.

Is there a way to suppress pipe error, and just get the return code from make installer?

3

4 Answers 4

15

A 141 exit code indicates that the process failed with SIGPIPE; this happens to yes when the pipe closes. To mask this for your CI, you need to mask the error using something like

(yes phrase ||:) | make installer

This will run yes phrase, and if it fails, run : which exits with code 0. This is safe enough since yes doesn’t have much cause to fail apart from being unable to write.

To debug pipe issues such as these, the best approach is to look at PIPESTATUS:

yes phrase | make installer || echo "${PIPESTATUS[@]}"

This will show the exit codes for all parts of the pipe on failure. Those which fail with exit code 141 can then be handled appropriately. The generic handling pattern for a specific error code is

(command; ec=$?; if [ "$ec" -eq 141 ]; then exit 0; else exit "$ec"; fi)

(thanks Hauke Laging); this runs command, and exits with code 0 if command succeeds or if it exits with code 141. Other exit codes are reflected as-is.

8
  • 3
    And for the paranoid: ( yes; ec=$?; if [ "$ec" -eq 141 ]; then exit 0; else exit "$ec"; fi ) | make installer ;-) Commented Apr 27, 2020 at 13:18
  • (yes ||:) | tee >(echo yo) still returns 141
    – carbolymer
    Commented Apr 27, 2020 at 13:45
  • 2
    If make stops reading, that will cause yes to fail with a SIGPIPE, not make. make will fail with a SIGPIPE if it can’t write to a pipe. Commented Apr 27, 2020 at 13:50
  • 2
    Or (cmd; exit "$(( $? == 141 ? 0 : $? ))") Commented Jul 15, 2022 at 17:45
  • 2
    See also [ "$(kill -l "$?")" = PIPE ] to avoid having to hard code that 141. Commented Jul 15, 2022 at 17:48
3

Is there a way to suppress pipe error, and just get the return code from make installer?

An alternative solution to the other excellent answers already posted, if you don't want to run your whole command pipe in a subshell (e.g. you want to be able to set variables):

yes phrase | make installer || { ec=$?; [ $ec -eq 141 ] && true || (exit $ec); }

# generic syntax:
cmd1 | cmd2 | cmd3 || { ec=$?; [ $ec -eq 141 ] && true || (exit $ec); }

This uses the exit command in a subshell to preserve the original exit code from the command pipe intact, if it's not 141. Thus, it will have the intended effect if set -e (set -o errexit) is in effect along with set -o pipefail.

We can use a function for cleaner code, which allows the use of return instead of the exit in a subshell trick:

handle_pipefails() { 
    # ignore exit code 141 from simple command pipes
    # - use with: cmd1 | cmd2 || handle_pipefails $?
    (( $1 == 141 )) && return 0
    return $1
}

# then use it or test it as:
yes | head -n 1 || handle_pipefails $?
echo "ec=$?"

# then change the tested code from 141 to e.g. 999 in
# the function, and see that ec was in fact captured as
# 141

An alternative, if you wanted to test the exit code of other commands involved in a more complex pipe, could be to test the whole PIPESTATUS:

handle_pipefails2() {
    # ignore exit code 141 from more complex command pipes
    # - use with: cmd1 | cmd2 | cmd3 || handle_pipefails2 "${PIPESTATUS[@]}"
    for x in "$@"; do
        (( $x == 141 )) || { (( $x > 0 )) && return $x; }
    done
    return 0
}
2
  • handle_pipefails() could be even simpler: handle_pipefails() { (( $1 == 141 )) || return $1; }
    – intelfx
    Commented Jan 19 at 4:28
  • Nice one, thanks @intelfx. Commented Jan 19 at 15:13
2
( yes phrase ; exit 0 ) | make installer
2
  • 1
    does not make a difference, (yes ; exit 0) | tee >(echo yo) still returns 141
    – carbolymer
    Commented Apr 27, 2020 at 13:43
  • @carbolymer That's not what happens on my system. Make sure the exit code does not come from make: ( yes phrase ; exit 0 ) | ( make installer ; echo "make exit code: ${?}") Commented Apr 27, 2020 at 14:31
0

After some digging, I've found this answer: https://stackoverflow.com/questions/22464786/ignoring-bash-pipefail-for-error-code-141#comment60412687_33026977 Basically, using:

set -euxo pipefail
yes phrase | make installer || (ec=$? ; if [ "$ec" -eq 141 ]; then exit 0; else exit "$ec"; fi)

just filters out SIGPIPE from return codes.

1
  • 2
    This will hide the real exit code for any other failure... Commented Apr 27, 2020 at 13:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .