问题

当在subshell内设置了'extglob'选项时,Bash脚本的执行失败并显示以下错误消息:

/tmp/foo.sh: line 7: syntax error near unexpected token `('

#!/usr/bin/env bash
set -euo pipefail
(
    shopt -s extglob
    for f in ?(.)!(|+(.)|vendor); do
        echo "$f"
    done
)

它在function内部以相同的方式失败:

#!/usr/bin/env bash
set -euo pipefail

list_no_vendor () {
    shopt -s extglob
    for f in ?(.)!(|+(.)|vendor); do
        echo "$f"
    done
}

list_no_vendor

调查

在这两种情况下,当全局设置选项(子 shell 程序或函数的之外的)时,脚本都会成功执行。

令人惊讶的是,在本地设置时,'extglob'选项似乎在子 shell 程序和函数中均被有效启用:

#!/usr/bin/env bash
set -euo pipefail

(
    shopt -s extglob
    echo 'In the subshell:' "$(shopt extglob)"
)

list_no_vendor () {
    shopt -s extglob
    echo 'In the function:' "$(shopt extglob)"
}

echo 'In the main shell:' "$(shopt extglob)"

list_no_vendor

输出:

In the subshell: extglob            on
In the main shell: extglob          off
In the function: extglob            on

这使语法错误令我极为困惑。

解决方法

将heredoc传递给bash命令即可。

#!/usr/bin/env bash
set -euo pipefail

bash <<'EOF'
    shopt -s extglob
    echo 'In the child:' "$(shopt extglob)"
EOF

echo 'In the parent:' "$(shopt extglob)"

输出:

In the child: extglob           on
In the parent: extglob          off

但是,我很想了解这里的问题要点。

最佳答案

extglob是解析器使用的标志。函数,复合命令和&c。会在执行之前完整解析。因此,必须先解析extglob,然后再解析该内容。在执行时设置它,但在解析时间之后对先前解析的内容没有任何影响。

这也是为什么您不能单行运行shopt -s extglob; ls !(*.txt)的原因(以前未设置extglob的情况),但是两个命令之间必须有换行符。

并不是作为可接受的做法的示例,而是作为演示行为的示例,请考虑以下内容:

#!/usr/bin/env bash
(
    shopt -s extglob
    # Parse of eval'd code is deferred, so this succeeds
    eval '
        for f in ?(.)!(|+(.)|vendor); do
            echo "$f"
        done
    '
)

这里没有发生此类错误,因为仅在执行eval之后才解析传递给shopt -s extglob的内容,而不是解析要在子shell中运行的代码块。

关于当在子shell或函数中设置了 'extglob'选项时,Bash脚本抛出语法错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49283740/

10-12 17:47