bash将通配符扩展与大括号扩展相结合


8

我正在尝试扩展一个字符串,该字符串涉及通配符和括号内指定的扩展名集合。如以下示例所示,似乎没有任何效果。变量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"

fwiw eval ls $secondList在这里工作正常...您要完成什么?
don_crissti

您需要检查bash扩展顺序 -大括号扩展发生参数扩展之前。为了获得想要的效果,您需要进行eval第二轮扩展。
格伦·杰克曼(Glenn Jackman)'16


@glennjackman有一个没有评估的解决方案。请看我答案结尾第二个解决方案

Answers:


7

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

同样,括号扩展名也需要取消引用才能被外壳扩展。

这项工作(让我们使用echo来查看shell的功能):

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

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

$ 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

为什么行得通?

重要的是我们了解为什么会起作用。这是因为扩展的顺序。首先是“括号扩展”,然后是(最后一个)“路径名扩展”(aka glob-expansion)。

Brace --> Parameter (variable) --> Pathname

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

$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2

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

$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

问题是我们不能将变量用于括号扩展。在“括号扩展”中使用变量
之前,已经进行了多次解释。

要扩展作为“变量扩展”的结果的“括号扩展”,您需要使用来将命令行重新提交给shell eval

$ list={ext1,ext2}
$ eval echo '*.'"$list"

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

文件名的值不会对eval执行任何问题:

$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2

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

#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"

更好的选择。

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

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

注意:请注意,两种解决方案(评估和模式)(按书面规定)对于带有空格或换行符的文件名都是安全的。但是对于$list带空格的,将失败,因为$list未加引号或eval删除了引号。


是的,扩展了范围
格伦·杰克曼(Glenn Jackman)'16

2

考虑:

secondList='*.{ext1,ext2}'
ls $secondList 

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

这是因为,当bash第一次看到命令行时,没有括号。之后secondList展开,为时已晚。

以下将起作用:

$ s='*'
$ ls $s.{ext1,ext2}
a.ext1  a.ext2  b.ext1  b.ext2

在此,命令行具有大括号,因此可以将大括号展开作为第一步。之后,将的值$s替换为(变量扩展),最后执行路径名扩展

文献资料

man bash 说明了扩展顺序:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.