Bash中两种@扩展列表写法为何执行结果不一致?
两种Extglob写法在Bash脚本中的差异解析
问题场景
你编写的Bash脚本中,以下ls命令可正常列出./WORKS目录下指定后缀的文件:
#!/bin/bash testor=$(shopt | grep extglob | awk '{ print $2 }' ) if [ "${testor}" = "off" ] then shopt -s extglob shopt | grep extglob fi ls ./WORKS/@(*.sh|*.dat|*.txt)
但将ls命令改为以下写法时,会出现No such file or directory错误:
ls @(./WORKS/*.sh|./WORKS/*.dat|./WORKS/*.txt)
核心差异原因
两种写法的本质是extglob模式的作用对象不同:
第一种写法:
./WORKS/@(*.sh|*.dat|*.txt)
这里的@()仅作用于./WORKS/目录下的文件名。shell会先定位到./WORKS/目录,再用@(*.sh|*.dat|*.txt)匹配该目录下所有后缀为.sh、.dat或.txt的文件,最终把所有匹配到的文件路径合并成列表传给ls,完全符合extglob针对文件名匹配的设计逻辑。第二种写法:
@(./WORKS/*.sh|./WORKS/*.dat|./WORKS/*.txt)
这里的@()包裹的是完整路径模式,shell会按「或」逻辑依次处理每个分支:- 先尝试扩展
./WORKS/*.sh——如果目录下没有.sh文件,这个模式会保留字面量./WORKS/*.sh; - 再依次处理另外两个分支,同样如果没有匹配文件就保留字面量。
最终ls会收到这些未被正确扩展的字面路径,而系统中不存在名为./WORKS/*.sh的文件,因此报错。
此外,extglob的
@()是选择单个匹配的模式,而非合并所有分支的匹配结果。哪怕某个分支能匹配到文件,其他分支的未匹配字面量仍会被传递给ls,导致错误。- 先尝试扩展
内容的提问来源于stack exchange,提问作者Eric Marceau
相关产品推荐
相关产品推荐

