2

I just found the following results in bash (version 4.2.25(1)-release):

$ true; echo "${PIPESTATUS[@]}"
0
$ ! true; echo "${PIPESTATUS[@]}"
0
$ false; echo "${PIPESTATUS[@]}"
1
$ ! false; echo "${PIPESTATUS[@]}"
1
$ true && false; echo "${PIPESTATUS[@]}"
1
$ true && ! false; echo "${PIPESTATUS[@]}"
1

So, $PIPESTATUS seems to ignore negations in all cases. Is this a known issue? I couldn't find anything about it. Or is this a wanted behavior? If so, what's the reasoning behind it?

When using a subshell, everything works as I would have expected it:

$ (true && ! false); echo "${PIPESTATUS[@]}"
0
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • Just a remark: `$?` does not have this issue, it has the expected value (0 for `! false` etc.), so it is not part of this question. – Alfe Apr 06 '16 at 09:17
  • `$?` >Expands to the exit status of the most recently executed foreground pipeline. `$PIPESTATUS` > exit status values from the processes. Maybe it only counts the false/true true part as a process. – 123 Apr 06 '16 at 09:31
  • Yeah, appears so. For a `! a | b | c | d` the `$PIPESTATUS` holds the results of the `a`, the `b`, the `c`, and the `d`, but not of the negation. In my scenario (`grep a x && ! grep b y`, actually) this produced rather peculiar results. – Alfe Apr 06 '16 at 09:36

2 Answers2

3

I think this behavior is intended and becomes clearer if viewed with a complete pipeline:

a | b | c | d ; echo "${PIPESTATUS[@]}"

This will show the exit statuses of the processes a, b, c, and d.

Negations, now, only apply to the exit status of a complete pipeline:

! a | b | c | d ; echo "${PIPESTATUS[@]}"

It is not allowed to negate parts of the pipeline:

a | ! b | c | d   # Syntax error at "| !"

Because of this approach, the pipe associates stronger than the negation (one can say that this is a ! (a | b | c | d) on the association level), so the results of the pipe constituents are not affected by the negation because the negation is applied later, after evaluating the pipeline.

Alfe
  • 56,346
  • 20
  • 107
  • 159
2

! a | b | c is interpreted as !{a | b | c;} by the shell & not as {! a;} | b | c.

Individual command exit statuses are stored in ${PIPESTATUS[@]}.

The $? refers to exit-status of the entire command, including !.

You can force (! a) | b | c by actually spawning the subshell. e.g.

$ true; echo "${PIPESTATUS[@]}"
0
$ (! true); echo "${PIPESTATUS[@]}"
1
$ false; echo "${PIPESTATUS[@]}"
1
$ (! false); echo "${PIPESTATUS[@]}"
0
anishsane
  • 20,270
  • 5
  • 40
  • 73