bash 结合通配符扩展和大括号扩展

Leo*_*mon 11 bash brace-expansion

我正在尝试扩展包含通配符和大括号内指定的扩展集合的字符串。如下例所示,似乎没有任何效果。变量firstList扩大罚款,但没有secondListthirdList还是fourthList扩展正确。我也尝试过各种版本,eval但都没有奏效。任何帮助,将不胜感激

#!/bin/bash
touch a.ext1
touch b.ext1
firstList='*.ext1'
ls  $firstList
touch a.ext2
touch b.ext2
secondList='*.{ext1,ext2}'
ls $secondList 
ls '$secondList'
ls "$secondList"
thirdList=*.{ext1,ext2}
ls $thirdList  
ls '$thirdList'
ls "$thirdList"
fourthList="*.{ext1,ext2}"
ls $fourthList
ls '$fourthList'
ls "$fourthList"
Run Code Online (Sandbox Code Playgroud)

小智 9

外壳扩展*仅当外壳未引述任何引用停止扩张。

此外,大括号扩展需要不加引号才能被 shell 扩展。

这项工作(让我们使用 echo 查看 shell 做了什么):

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Run Code Online (Sandbox Code Playgroud)

即使有一些其他名称的文件:

$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1  a.ext2  b.ext1  b.ext2  c.ext3  c.ext4  d.ext3  d.ext4  none

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

重要的是我们要了解它为什么起作用。这是因为扩展的顺序。首先是“支撑扩展”,然后是(最后一个)“路径名扩展”(又名全局扩展)。

Brace --> Parameter (variable) --> Pathname
Run Code Online (Sandbox Code Playgroud)

我们可以暂时关闭“路径名扩展”:

$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2
Run Code Online (Sandbox Code Playgroud)

“路径名扩展”接收两个参数:*.ext1*.ext2

$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Run Code Online (Sandbox Code Playgroud)

问题是我们不能为大括号扩展使用变量。
之前已经多次解释在“支撑扩展”中使用变量

要扩展作为“变量扩展”结果的“大括号扩展”,您需要使用eval.

$ list={ext1,ext2}
$ eval echo '*.'"$list"
Run Code Online (Sandbox Code Playgroud)

大括号 -->变量--> Glob || -->大括号--> 变量 --> Glob
........这里引用 -->^^^^^^ || 评估 ^^^^^^^^^^^^^^^^^^^^^^^^^

文件名的值不会给 eval 带来执行问题:

$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2
Run Code Online (Sandbox Code Playgroud)

但是 的值$list可能是不安全的。但是, 的值$list由脚本编写者设置。脚本编写者控制eval: 只是不使用外部设置的$list. 尝试这个:

#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"
Run Code Online (Sandbox Code Playgroud)

更好的选择。

另一种选择(没有 eval)是使用 Bash“扩展模式”

#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list
Run Code Online (Sandbox Code Playgroud)

注意:请注意,对于带有空格或换行符的文件名,两种解决方案(eval 和模式)(如所写)都是安全的。但是对于$list带有空格的会失败,因为$list没有引用或 eval 删除了引号。


Joh*_*024 2

考虑:

secondList='*.{ext1,ext2}'
ls $secondList 
Run Code Online (Sandbox Code Playgroud)

问题是大括号扩展是在变量扩展之前 完成的。这意味着,在上面,从不执行大括号扩展。

这是因为,当 bash 第一次看到命令行时,没有大括号。等secondList扩大之后,就晚了。

以下将起作用:

$ s='*'
$ ls $s.{ext1,ext2}
a.ext1  a.ext2  b.ext1  b.ext2
Run Code Online (Sandbox Code Playgroud)

这里,命令行有大括号,以便第一步可以执行大括号扩展。之后,$s将 的值代入(变量扩展),最后进行路径名扩展。

文档

man bash解释扩展的顺序:

展开的顺序是:大括号展开;波形符扩展、参数和变量扩展、算术扩展和命令替换(以从左到右的方式完成);分词;和路径名扩展。