我发现extglob
仅在复合化合物中设置 shell 选项会导致后续扩展 glob 失败。是否需要在复合命令之外设置 shell 选项?我在 Bash 手册页中没有看到这种要求的迹象。
例如,以下脚本可以正常工作(打印a.0 a.1
):
#!/bin/bash
touch a.0 a.1 \
a.b.0 a.b.1
shopt -s extglob
ls "a."!(b*)
但是,如果最后两行作为复合命令执行,则脚本将失败并出现以下错误:
syntax error near unexpected token `('
` ls "a."!(b*)'
这是使用从 4.2 到 4.4 的 Bash 版本以及各种复合命令进行测试的:
(1) 有条件的——if
#!/bin/bash
touch a.0 a.1 \
a.b.0 a.b.1
if true; then
shopt -s extglob
ls "a."!(b*)
fi
(2) 大括号——{ }
#!/bin/bash
touch a.0 a.1 \
a.b.0 a.b.1
{
shopt -s extglob
ls "a."!(b*)
}
(3) 子壳—— ( )
:
#!/bin/bash
touch a.0 a.1 \
a.b.0 a.b.1
(
shopt -s extglob
ls "a."!(b*)
)
在所有情况下,如果将shopt
移到复合命令之外,则脚本会成功。
在整个命令被解析之前,复合命令的任何部分都不会执行,这在手册的“外壳操作”部分中有所记录:所有标记化和解析都发生在“the”命令执行之前。启用
extglob
通过添加新的模式匹配运算符来更改语言语法,这些运算符在标记化过程中被识别。因为在到达
shopt
时命令尚未执行!(
,所以它被视为尝试的历史扩展 (!
) 或控制运算符(
,而不是!(
被视为单个项目(对于@(
等,除了没有历史扩展之外,那里)。当解析到达该标记时,任何一种情况通常都会出错,尽管
!(...)
在行的开头或之后time
是一个否定的子shell管道。"unexpected token `('
" 意味着它不能接受那个地方的子shell表达式。当您希望仅在子 shell 中临时启用 extglob 时,这有点烦人。一种解决方法是定义一个使用所需 glob 的函数,然后重新禁用 extglob,然后从启用 extglob 的子 shell 调用该函数:
它非常笨重。
如果您在复合命令中创建别名,也会发生相同的效果,因为它们也在解析之前被处理:
将打印
foo: command not found
, 然后xyz
,因为别名已创建,但在完成之前不可用if
。