【问题标题】:In bash shell -E option explanation, what does "any trap inherited by a subshell environment" mean?在 bash shell -E 选项解释中,“子shell环境继承的任何陷阱”是什么意思?
【发布时间】:2021-02-27 08:15:59
【问题描述】:

这是来自 bash 设置选项的手册(适用于 set -E

-E

如果设置,ERR 上的任何陷阱都将由 shell 函数、命令替换和在子 shell 环境中执行的命令继承。在这种情况下,ERR 陷阱通常不会被继承。

“ERR 陷阱”是什么意思? “子shell环境中的shell函数继承了ERR陷阱”是什么?即使在陷阱和陷阱传递给子shell之后,shell脚本也可以继续吗?有人可以用一个简单的例子来详细说明一下吗?

【问题讨论】:

    标签: bash bash-trap


    【解决方案1】:

    trap 上的 Bash 手册说:

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

    如果 sigspecERR,则只要管道(可能包含单个简单命令)、列表或复合命令返回非零退出,就会执行命令 arg状态,受以下条件限制。如果失败的命令是紧跟在untilwhile 关键字之后的命令列表的一部分,则不会执行ERR 陷阱,这是ifelif 保留字之后的测试的一部分,是命令的一部分在&&|| 列表中执行,除了最后一个&&|| 之后的命令,管道中除最后一个之外的任何命令,或者如果命令的返回状态正在使用! 反转。这些与errexit (-e) 选项所遵循的条件相同。

    您引用的材料说trap '…' ERR 通常不会被shell 函数、命令替换或在子shell 环境中执行的命令继承。但是,如果您使用set -E,则ERR 陷阱将被继承。

    这句话的意思是:

    • 如果设置了set -E,则 ERR 上的任何陷阱都会被 shell 函数继承。
    • 如果设置了 set -E,则 ERR 上的任何陷阱都会被命令替换继承。
    • 如果设置了set -E,则ERR 上的任何陷阱都会被在子shell 环境中执行的命令继承。

    'in a subshel​​l environment' 子句不适用于 shell 函数或命令替换。

    我无法解析您的问题“即使在陷阱和陷阱传递给子shell 之后,shell 脚本还能继续执行吗?” shell 脚本可以在陷阱之后继续执行 — 发生的情况取决于 trap 命令的 arg 部分中的命令。

    这是一个演示 set -E 实际操作的 shell 脚本:

    #!/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
    

    运行时会产生:

    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
    

    如您所见,ERR 陷阱会在失败的 ls 命令自行运行时触发,但在函数或子 shell 中运行或作为(某些)命令替换的一部分时不会失败。

    当设置了set -E 选项时,ERR 陷阱会在函数、子shell 和命令替换中的命令失败时触发。

    奇怪的是,ERR 陷阱确实会在像x=$(ls /non-existent) 这样的命令替换中触发,但在上面显示的更复杂的示例中不会触发。我不清楚这是否应该发生。 我正在使用GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18) — 更高版本的 Bash (4.x) 的行为可能不同。

    【讨论】:

    • 啊,这就解释了。在我的情况下,有一个区别:子shell在设置-E之前继承了陷阱。无论如何,现在我可以理解了。非常感谢!
    【解决方案2】:

    Jonathan Leffler 共享的脚本中的小改动。编辑将包含使用-E 选项集直接执行的ls(无需命令替换、函数调用和子shell)。此外,附加的是在bash 5.0.17(1)-release 上执行的输出。在这个输出中,可以看到TRAP 在所有四种情况下都被触发,即直接从脚本执行命令、在函数中、在子shell 中以及在命令替换中。而在Jonathan Leffler 共享的输出中,即使设置了-E 标志,TRAP 似乎也不会因命令替换和子shell 而被触发。

    脚本:

    #! /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
    echo ""
    
    func()
    {
        echo begin-func
        ls /non-existent
        echo end-func
    }
    
    echo invoke-func
    func
    echo finish-func
    echo ""
    
    echo begin-substitution
    echo $(date): $(ls /non-existent)
    echo end-substitution
    echo ""
    
    echo begin-subshell
    (ls /non-existent)
    echo end-subshell
    
    echo "************ Set -E ****************"
    set -E
    echo begin-ls
    ls /non-existent
    echo end-ls
    echo ""
    
    echo invoke-func
    func
    echo finish-func
    echo ""
    
    echo begin-substitution
    echo $(date): $(ls /non-existent)
    echo end-substitution
    echo ""
    
    echo begin-subshell
    (ls /non-existent)
    echo end-subshell
    

    输出: 开始-ls ls: cannot access '/non-existent': 没有这样的文件或目录 陷阱错误 结束-ls

    invoke-func
    begin-func
    ls: cannot access '/non-existent': No such file or directory
    end-func
    finish-func
    
    begin-substitution
    ls: cannot access '/non-existent': No such file or directory
    Monday 31 January 2022 01:45:06 PM IST:
    end-substitution
    
    begin-subshell
    ls: cannot access '/non-existent': No such file or directory
    TRAP ERR
    end-subshell
    ************ Set -E ****************
    begin-ls
    ls: cannot access '/non-existent': No such file or directory
    TRAP ERR
    end-ls
    
    invoke-func
    begin-func
    ls: cannot access '/non-existent': No such file or directory
    TRAP ERR
    end-func
    finish-func
    
    begin-substitution
    ls: cannot access '/non-existent': No such file or directory
    TRAP ERR
    Monday 31 January 2022 01:45:06 PM IST:
    end-substitution
    
    begin-subshell
    ls: cannot access '/non-existent': No such file or directory
    TRAP ERR
    end-subshell
    

    【讨论】:

      猜你喜欢
      • 2021-05-22
      • 1970-01-01
      • 2016-12-22
      • 1970-01-01
      • 2010-09-09
      • 2020-07-06
      • 2015-02-20
      • 1970-01-01
      相关资源
      最近更新 更多