65

Can someone explain why I get exit code 141 from the below?

#!/usr/bin/bash

set -o pipefail

zfs list | grep tank
echo a ${PIPESTATUS[@]}

zfs list | grep -q tank
echo b ${PIPESTATUS[@]}

cat /etc/passwd | grep -q root
echo c ${PIPESTATUS[@]}

I get

...
a 0 0
b 141 0
c 0 0

From my understanding exit code 141 is a failure, but the line above gives zero, so it should be success, I would say.

Sandra Schlichting
  • 24,425
  • 31
  • 103
  • 150

3 Answers3

93

This is because grep -q exits immediately with a zero status as soon as a match is found. The zfs command is still writing to the pipe, but there is no reader (because grep has exited), so it is sent a SIGPIPE signal from the kernel and it exits with a status of 141.

Another common place where you see this behaviour is with head. e.g.

$ seq 1 10000 | head -1
1

$ echo ${PIPESTATUS[@]}
141 0

In this case, head read the first line and terminated which generated a SIGPIPE signal and seq exited with 141.

See "The Infamous SIGPIPE Signal" from The Linux Programmer's Guide.

Community
  • 1
  • 1
dogbane
  • 254,755
  • 72
  • 386
  • 405
  • 38
    Conventionally, an exit status *N* greater than 128 indicates the program was terminated by signal *N* - 128. Since `SIGPIPE` is signal 13, 141 - 128 = 13 indicates your program was ended by a `SIGPIPE`. – chepner Oct 01 '13 at 16:15
  • 17
    Is there a way I can still use `set -o pipefail` and `grep -q`, as I would like to keep it, as I have lots of parsing from SSH. – Sandra Schlichting Oct 01 '13 at 16:22
  • 4
    @chepner: It's not a matter of convention, it's matter of how shells deal with processes that exit due to a signal. For bash, it's 128 + signal_number, but other shells use other formulas. See this excellent post: http://unix.stackexchange.com/a/99134/9041 – Flimm Aug 13 '14 at 09:46
  • 3
    @SandraSchlichting: See [Effective SIGPIPE handling](http://www.pixelbeat.org/programming/sigpipe_handling.html) for issues with pipefail in bash and alternative to it. – akhan May 24 '16 at 19:50
  • 2
    @SandraSchlichting: I have the same issue and it is very annoying. One idea: Do not use `-q` option with `grep`, but redirect output: `1> /dev/null 2>&1`. In theory, slightly slower as `grep` will process entire input, but, practically: it works. – kevinarpe May 08 '17 at 10:15
  • @chepner This just made so much sense to me, after years of never actually going and looking for that answer. Thank you! – Tom Granot May 16 '19 at 16:58
  • 1
    To avoid exit 141 with `head -c` (counting bytes), replace it with `cut -b start_byte-end_byte`. – Victor Sergienko May 22 '19 at 19:55
  • 2
    An interesting if verbose solution to `SIGPIPE` and `pipefail` is presented here: https://unix.stackexchange.com/a/582850/66344 – Tad Lispy Jun 22 '20 at 07:57
  • If you are just wanting the first line of the result, then `head -n 1` can be replaced with `sed -n 1p` which doesn't have this failure. – balupton Jul 03 '21 at 18:38
6

I'm not familiar with zfs list, but I guess it complains about its standard output being closed - grep -q exits immediately when a match is found, unlike grep.

volferine
  • 362
  • 1
  • 8
3

Another option would be to not use a pipe, but use a process substitution:

grep -q tank <(zfs list)

Update: I guess is the same thing, as the process run inside parentheses will also receive sigpipe.