bug-bash
[Top][All Lists]
Advanced

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

Re: declare XXX=$(false);echo $?


From: Martin D Kealey
Subject: Re: declare XXX=$(false);echo $?
Date: Sat, 3 Dec 2022 11:26:10 +1000

On Fri, 2 Dec 2022 at 20:28, Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
wrote:

> Surprisingly "declare XXX=$(false);echo $?" outputs "0" (not "1")
>

"Surprising" is subjective.

There is no indication in the manual page that "declare" ignores the exit
> code of commands being executed to set values.
>

Framing this as `declare` "ignoring" something indicates a possible
misunderstanding of the separate phases of execution. Every command (with
exactly three exceptions) sets its own exit status without regard for any
previous value of $?, including any set by $(...) expansions used to create
the command in question.

If anything is weird, it's that simple (bare) assignments set $? to 0
UNLESS there's a command substitution providing a status. The other two
exceptions are `return` and `exit`, which use the previous $? as the
default if no parameter is supplied.

Like every other command, including "declare", sets its exit status
independently of what $? is before it runs. So it's somewhat of a mystery
why anyone would expect "declare" to be any different.

(Actually, it's no mystery: cargo-cult coding is the norm, as the majority
of people writing shell scripts have not read all of "man bash" and have
little idea of how the shell is actually supposed to work. So perhaps Bash
needs an officially sanctioned "beginners' guide".)

Found in the real code (intended to trigger a bug):
>     declare ERRORS=0 ARGS=$(getopt -o "$S_OPTS" -l "$L_OPTS" -n "$0" --
> "$@")
>
    if [ $? -ne 0 ]; then
>         usage
>     fi
>

That is a well-known anti-pattern that should be avoided.

Some working equivalents would be:
```
    declare ARGS=$( getopt -o "$S_OPTS" -l "$L_OPTS" -n "$0" -- "$@" )
ERRORS=$?
    (( ERRORS == 0 )) || usage
```
or:
```
    declare ARGS ERRORS=0
    ARGS=$( getopt -o "$S_OPTS" -l "$L_OPTS" -n "$0" -- "$@" ) ||
        usage
```

-Martin

(PS: "Working" would be questionable, since putting `getopt` in a subshell
prevents it from updating "$@"; and `test $? = 0` and its variants are
pointless clutter.)


reply via email to

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