问题
当在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/