bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: PIPESTATUS differs from $? for compound command


From: Martin D Kealey
Subject: Re: PIPESTATUS differs from $? for compound command
Date: Sat, 14 Dec 2024 17:56:19 +1000

I started writing a response early in this thread and then set it aside.
My initial impression was that excluding most compound commands from being
reported in PIPESTATUS was at best questionable, but now I'm thinking
otherwise.

It is obviously useful to be able to inspect PIPESTATUS after compound
commands such as:

   - ! PIPELINE
   - case $thing in pattern1) PIPELINE1 ;; pattern2) PIPELINE2 ;; *)
   PIPELINE3 ; esac
   - if condition ; then PIPELINE1 ; else PIPELINE2 ; fi
   - while condition ; do PIPELINE ; done

What seems more like a bug are cases like:

   - if FAIL ; then PIPELINE ; fi
   - while FAIL ; do PIPELINE ; done

where PIPESTATUS reflects whatever FAIL did, and even worse cases like:

   - case NOMATCH in ... esac
   - select NAME in CHOICES ; do ... ; done </dev/null

where PIPESTATUS is completely unaffected by the block, instead retaining
its previous values.

Much as I hate breaking changes, it is hard to see how these latter 3 could
be used reliably in valid code, and they invite some rather insidious bugs,
so I would like to propose a change:

*That after finishing a compound command, **PIPESTATUS and $? are (set in
ways that makes them appear to be) unaffected by **any command within any
controlling condition LIST (before “do” or “then”), and if no (other)
pipeline was executed inside the compound command**, $? is set to 0, and**
PIPESTATUS is set to an empty list.*

(This would not actually change the current behaviour of $?, but
documenting PIPESTATUS and $? together like this would make it clearer when
and how they differ.)

The idea is that having PIPESTATUS be empty would indicate that *nothing*
was executed; this would apply:

   - when the controlling condition is false in an “if/fi” block without an
   “else” clause;
   - when a loop has zero iterations (not counting initially evaluating the
   controlling condition LIST);
   - when a case/esac block has a WORD that does not match any of its
   PATTERNs;
   - (new syntax, for consideration) when ‘!’ is bare, with nothing between
   it and the next command terminator

Then ${#PIPESTATUS[@]}==0 could conceivably be useful as a post-facto
indication of zero iterations.

Rationale: whatever is done, even leaving it unchanged, there is going to
be *some* surprise; I think this proposal at least minimizes the surprise,
since it means that the relationship between $? and PIPESTATUS is a bit
less arcane.

If there are compatibility concerns, it would be acceptable for this to be
gated on a shopt such as “empty_pipestatus” or “not old_pipestatus” or
perhaps “not compat53 or earlier” (or equivalently, “compat54 or later”).

-Martin

PS: before I considered setting PIPESTATUS to empty, I did wonder about
simply making all compound commands transparent to PIPESTATUS (with the
same exclusion of controlling conditions), but it quickly became evident
that this would not yield a “better” result -- and would be much more
complicated to implement.

PPS: some examples:

$ *while ( exit 42 ) ; do exit 43 | ( exit 44 ) ; done* ; declare -p
PIPESTATUS
declare -a PIPESTATUS='([0]="42")'
$ *if ( exit 42 ) ; then exit 43 | ( exit 44 ) ; fi* ; declare -p PIPESTATUS
declare -a PIPESTATUS='([0]="42")'
$ ( exit 42 ) ; *case NOMATCH in esac* ; declare -p PIPESTATUS
declare -a PIPESTATUS='([0]="42")'
$ ( exit 42 ) ; *select FOO in BAR ; do echo $FOO ; ( exit 43 ) ; done
</dev/null* ; declare -p PIPESTATUS
1) BAR
#?
declare -a PIPESTATUS='([0]="42")'
$ set -- ; ( exit 42 ) ; *for X do exit 43 | ( exit 44 ) ; done* ; declare
-p PIPESTATUS
declare -a PIPESTATUS='([0]="42")'


reply via email to

[Prev in Thread] Current Thread [Next in Thread]