Estou tentando expandir uma string envolvendo um curinga e uma coleção de extensões especificadas entre colchetes. Nada parece funcionar como ilustra o exemplo abaixo. a variável firstList
se expande bem, mas nenhuma delas secondList
ou thirdList
se fourthList
expande corretamente. Eu também tentei várias versões, eval
mas nenhuma funcionou. Qualquer ajuda seria apreciada
#!/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"
Responder1
Oconchaexpande *
apenas se não estiver entre aspas, qualquer cotação interrompe a expansão pelo shell.
Além disso, uma expansão de chave precisa não estar entre aspas para ser expandida pelo shell.
Este trabalho (vamos usar 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 Brace" e depois (a última) "Expansão do nome do caminho" (também conhecida como expansão glob).
Brace --> Parameter (variable) --> Pathname
Podemos desligar 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 das chaves.
Já foi explicado muitas vezes antes parausando uma variável dentro de uma "Brace Expansion"
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"
Braçadeira -->Variável--> Globo || -->Braçadeira--> Variável -->globo
........ citado aqui -->^^^^^^ || avaliação ^^^^^^^^^^^^^^^^^^^^^^^^^
Os 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 ser inseguro. No entanto, o valor de $list
é definido pelo escritor do roteiro. O escritor do script está no controle de eval
: Apenas não usar valores definidos externamente para $list
. Experimente 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) éuse Bash "Padrões Estendidos":
#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list
Nota: Esteja ciente de que ambas as soluções (eval e padrões) (conforme escritas) são seguras para nomes de arquivos com espaços ou novas linhas. Mas falhará $list
com espaços, porque $list
não está entre aspas ou o eval remove as aspas.
Responder2
Considerar:
secondList='*.{ext1,ext2}'
ls $secondList
O problema é queexpansão de cintaé feitoantes expansão variável. Isso significa que, acima, a expansão de chaves nunca é executada.
Isso ocorre porque, quando o bash vê a linha de comando pela primeira vez, não há colchetes. Depois de secondList
expandido, é tarde demais.
O seguinte funcionará:
$ s='*'
$ ls $s.{ext1,ext2}
a.ext1 a.ext2 b.ext1 b.ext2
Aqui, a linha de comando tem colchetes para queexpansão de cintapode ser executado como o primeiro passo. Depois disso, o valor de $s
é substituído em (expansão variável), e por fimexpansão do nome do caminhoé desempenhado.
Documentação
man bash
explica a ordem de expansão:
A ordem das expansões é: expansão de chaves; expansão de til, expansão de parâmetros e variáveis, expansão aritmética e substituição de comandos (feita da esquerda para a direita); divisão de palavras; e expansão do nome do caminho.