当在子shell或函数中设置了"extglob"选项时,Bash脚本抛出语法错误 [英] Bash script throws syntax errors when the 'extglob' option is set inside a subshell or function
问题描述
在子外壳:
/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
)
在函数内,它以相同的方式失败:
It fails in the same manner inside a 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
调查
在这两种情况下,当在子外壳程序或函数的外部中全局设置选项时,脚本都会成功执行.
Investigation
In both cases, the script executes successfully when the option is set globally, outside of the subshell or function.
令人惊讶的是,当在本地设置时,'extglob'选项似乎在子外壳程序和功能中均被有效启用:
Surprisingly, when set locally, the 'extglob' option appears to be effectively enabled in both the subshell and function:
#!/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
这使语法错误令我极为困惑.
This makes the syntax error extremely puzzling to me.
将Heredoc传递给 bash
命令有效.
Passing a heredoc to the bash
command works.
#!/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
但是,我很想了解这里的问题要点.
However I would be curious to understand the gist of the problem here.
推荐答案
extglob
是解析器使用的标志.功能,复合命令和& c.会在执行之前完整解析.因此,必须在解析内容之前,在 之前设置 extglob
.在执行时设置它,但在解析后设置,对于先前解析的内容没有任何作用.
extglob
is a flag used by the parser. Functions, compound commands, &c. are parsed in entirety ahead of execution. Thus, extglob
must be set before that content is parsed; setting it at execution time but after parse time does not have any effect for previously-parsed content.
这也是为什么您无法运行 shopt -s extglob;ls!(*.txt)
作为单行代码(以前未设置 extglob
时),但是两个命令之间必须有换行符.
This is also why you can't run shopt -s extglob; ls !(*.txt)
as a one-liner (when extglob
is previously unset), but must have a newline between the two commands.
不是作为可接受做法的示例,而是作为演示行为的示例,请考虑以下内容:
Not as an example of acceptable practice, but as an example demonstrating the behavior, consider the following:
#!/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中运行的代码块时.
No such error takes place here, because parsing of the content passed to eval
happens only after the shopt -s extglob
was executed, rather than when the block of code to be run in a subshell is parsed.
这篇关于当在子shell或函数中设置了"extglob"选项时,Bash脚本抛出语法错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!