O shell expande *
somente se for desmarcado, qualquer citação interrompe a expansão pelo shell.
Além disso, uma expansão de chave precisa estar sem aspas para ser expandida pelo shell.
Este trabalho (permite usar o echo para ver o que o shell faz):
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Mesmo se houver arquivos com outros nomes:
$ 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
Por que isso funciona?
É importante que entendamos por que isso funciona. É por causa da ordem de expansão. Primeiro a "Expansão de Brace" e depois (a última) "Expansão de Pathname" (a.k.a glob-expansion).
Brace --> Parameter (variable) --> Pathname
Podemos desativar a "expansão do nome do caminho" por um momento:
$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2
A "Expansão do nome do caminho" recebe dois argumentos: *.ext1
e *.ext2
.
$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
O problema é que não podemos usar uma variável para a expansão de chaves.
Foi explicado muitas vezes antes para usar uma variável dentro de uma "Expansão de Brace"
Para expandir uma "Expansão de Brace" que é o resultado de uma "Expansão de Variável", você precisa reenviar a linha de comando para o shell com eval
.
$ list={ext1,ext2}
$ eval echo '*.'"$list"
Brace --> Variable --> Glob || --> Brace --> Variable --> Glob
........ quoted here -->^^^^^^ || eval ^^^^^^^^^^^^^^^^^^^^^^^^^
Valores dos nomes dos arquivos não trazem problemas de execução para eval:
$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2
Mas o valor de $list
pode não ser seguro. No entanto, o valor de $list
é definido pelo script writer. O criador de scripts está no controle de eval
: apenas não use valores definidos externamente para $list
. Tente isto:
#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"
Uma alternativa melhor.
Uma alternativa (sem eval) é usar Bash "Extended Patterns" :
#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list
Nota: Por favor, esteja ciente de que ambas as soluções (eval e padrões) (como escritas) são seguras para nomes de arquivos com espaços ou novas linhas. Mas falhará em $list
com espaços, porque $list
não tem aspas ou o eval remove as aspas.