DISCLAIMER. English language used here only for compatibility (ASCII only), so any suggestions about my bad grammar (and not only it) will be greatly appreciated.

понедельник, 12 декабря 2011 г.

shell's 'errexit' option and variable definition.

Shell's option 'errexit' works unexpectedly when command returning non-zero
is called in `local` variable definition statement.

Let's see on example:

    f2()
    {
        echo -n "f2(): " >/dev/stderr
        set -o | grep errexit >/dev/stderr
        echo "subsh=$BASH_SUBSHELL" >/dev/stderr
        echo 'Failed' >/dev/stderr
        return 1
    }

    f1()
    {
        echo -n "f1(): " >/dev/stderr
        set -o | grep errexit >/dev/stderr
        echo A >/dev/stderr
        local v="$(f2)"
        echo B >/dev/stderr
        return 0
    }

    set -e
    echo -n "main(): " >/dev/stderr
    set -o | grep errexit >/dev/stderr

    f1
    exit 0


Run this script by bash:

    $ bash ./t.sh  ; echo r=$?
    main(): errexit         on
    f1(): errexit           on
    A
    f2(): errexit           off
    subsh=1
    Failed
    B
    r=0

Despite the f2()'s non-zero return code, script have not terminated
immediately after f2() call.  Also, you may notice, that 'errexit' is not set
in f2() (this is because bash's non-standard behavior (see 'COMMAND EXECUTION
ENVIRONMENT' section in bash(1); this is only the case in _not_ posix mode).
If, though, i run this script by dash, 'errexit' will be inherited by subshell
spawned by command substitution, but f2()'s non-zero return code still does
not abort the script:

    $ dash ./t.sh  ; echo r=$?
    main(): errexit         on
    f1(): errexit         on
    A
    f2(): errexit         on
    subsh=
    Failed
    B
    r=0

Now, let's try to call f2() in variable assignment instead. f1() function will
then look like

    f1()
    {
        echo -n "f1(): " >/dev/stderr
        set -o | grep errexit >/dev/stderr
        echo A >/dev/stderr
        local v=''
        v="$(f2)"
        echo B >/dev/stderr
        return 0
    }


Run by bash:

    $ bash ./t.sh  ; echo r=$?
    main(): errexit             on
    f1(): errexit               on
    A
    f2(): errexit               off
    subsh=1
    Failed
    r=1

and by dash:

    $ dash ./t.sh  ; echo r=$?
    main(): errexit         on
    f1(): errexit         on
    A
    f2(): errexit         on
    subsh=
    Failed
    r=1

And both of them abort script immediately after f2() call. So, it seems, like
the problem is in `local` (i don't say this is bug).

Note1.

Let's also test bash's `declare` builtin. First call f2() from command
substitution in `declare` variable definition statement:

    set -e
    echo -n "main(): " >/dev/stderr
    set -o | grep errexit >/dev/stderr

    echo AAA >/dev/stderr
    declare v="$(f2)"
    echo BBB >/dev/stderr

    exit 0

Run by bash

    $ bash ./t.sh  ; echo r=$?
    main(): errexit             on
    AAA
    f2(): errexit               off
    subsh=1
    Failed
    BBB
    r=0

and the result is the same as with `local`. Now, call f2() from variable
assignment:

    set -e
    echo -n "main(): " >/dev/stderr
    set -o | grep errexit >/dev/stderr

    echo AAA >/dev/stderr
    declare v=''
    v="$(f2)"
    echo BBB >/dev/stderr

    exit 0

and run by bash

    $ bash ./t.sh  ; echo r=$?
    main(): errexit             on
    AAA
    f2(): errexit               off
    subsh=1
    Failed
    r=1

and the result is again the same as with `local` and now it works.

Комментариев нет:

Отправить комментарий