bashbash-trap

In bash shell -E option explanation, what does "any trap inherited by a subshell environment" mean?


This is from the manual for bash set options (for set -E)

-E

If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.

What does "trap on ERR" mean? what does "trap on ERR is inherited by shell functions in a subshell environment"? Can a shell script proceed even after the trap and the trap is passed to a subshell? Can someone elaborate on this with a simple example, please?


Solution

  • The Bash manual on trap says:

    trap [-lp] [arg] [sigspec …]

    If a sigspec is ERR, the command arg is executed whenever a pipeline (which may consist of a single simple command), a list, or a compound command returns a non-zero exit status, subject to the following conditions. The ERR trap is not executed if the failed command is part of the command list immediately following an until or while keyword, part of the test following the if or elif reserved words, part of a command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return status is being inverted using !. These are the same conditions obeyed by the errexit (-e) option.

    The material you quote says that trap '…' ERR is not normally inherited by a shell function, a command substitution or commands executed in a subshell environment. However, if you use set -E, then the ERR trap is inherited.

    The sentence means:

    The 'in a subshell environment' clause does not apply to shell functions or command substitutions.

    I can't parse your question "Can a shell script proceed even after the trap and the trap is passed to a subshell?" A shell script can proceed after a trap — what happens depends on the commands in the arg part of the trap command.

    Here's a shell script that demonstrates set -E in action:

    #!/bin/bash
    #
    # SO 6485-2814 'trap ERR and set -E'
    
    trap 'echo TRAP ERR >&2' ERR
    
    echo begin-ls
    ls /non-existent
    echo end-ls
    
    func()
    {
        echo begin-func
        ls /non-existent
        echo end-func
    }
    
    echo invoke-func
    func
    echo finish-func
    
    echo begin-substitution
    echo $(date): $(ls /non-existent)
    echo end-substitution
    
    echo begin-subshell
    (ls /non-existent)
    echo end-subshell
    
    set -E
    
    echo invoke-func
    func
    echo finish-func
    
    echo begin-substitution
    echo $(date): $(ls /non-existent)
    echo end-substitution
    
    echo begin-subshell
    (ls /non-existent)
    echo end-subshell
    

    When run, it produces:

    begin-ls
    ls: /non-existent: No such file or directory
    TRAP ERR
    end-ls
    invoke-func
    begin-func
    ls: /non-existent: No such file or directory
    end-func
    finish-func
    begin-substitution
    ls: /non-existent: No such file or directory
    Sun Nov 15 23:16:25 MST 2020:
    end-substitution
    begin-subshell
    ls: /non-existent: No such file or directory
    end-subshell
    invoke-func
    begin-func
    ls: /non-existent: No such file or directory
    TRAP ERR
    end-func
    finish-func
    begin-substitution
    ls: /non-existent: No such file or directory
    Sun Nov 15 23:16:25 MST 2020:
    end-substitution
    begin-subshell
    ls: /non-existent: No such file or directory
    end-subshell
    

    As you can see, the ERR trap fires on the failing ls command run on its own, but doesn't fail when run in a function or in a subshell or as part of (some) command substitutions.

    When the set -E option is set, the ERR trap fires when the command failed in the function, in the subshell, and in the command substitution.

    Curiously, the ERR trap does fire in a command substitution like x=$(ls /non-existent) but not in the more complex example shown above. I'm not clear whether that's supposed to happen. I'm using GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18) — it's possible that later versions of Bash (4.x) do not behave the same.