在子shell中设置'extglob'选项时,Bash脚本的执行失败并显示以下错误消息:
/tmp/foo.sh: line 7: syntax error near unexpected token `('
Run Code Online (Sandbox Code Playgroud)
#!/usr/bin/env bash
set -euo pipefail
(
shopt -s extglob
for f in ?(.)!(|+(.)|vendor); do
echo "$f"
done
)
Run Code Online (Sandbox Code Playgroud)
它在函数内以相同的方式失败:
#!/usr/bin/env bash
set -euo pipefail
list_no_vendor () {
shopt -s extglob
for f in ?(.)!(|+(.)|vendor); do
echo "$f"
done
}
list_no_vendor
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,当在子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
Run Code Online (Sandbox Code Playgroud)
输出:
In the subshell: extglob on
In the main shell: extglob off
In the function: extglob on
Run Code Online (Sandbox Code Playgroud)
这使得语法错误对我来说非常令人费解.
将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)"
Run Code Online (Sandbox Code Playgroud)
输出:
In the child: extglob on
In the parent: extglob off
Run Code Online (Sandbox Code Playgroud)
但是我很想知道这里问题的要点.
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
'
)
Run Code Online (Sandbox Code Playgroud)
没有这样的错误发生在这里,因为传递到内容的解析eval只发生后的shopt -s extglob被执行,而不是当一个子shell解析要运行的代码块.