bash combinando expansão de curinga com expansão de chave

8

Estou tentando expandir uma string envolvendo um curinga e uma coleção de extensões especificadas dentro de chaves. Nada parece funcionar como o exemplo abaixo ilustra. a variável firstListexpande bem, mas não secondList, thirdListou fourthListse expande corretamente. Eu também tentei várias versões, evalmas nenhuma funciona. 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"
Leo Simon
fonte
O fwiw eval ls $secondListfunciona bem aqui ... o que você está tentando realizar?
31516 don_crissti
Você precisa verificar a ordem das expansões do bash - a expansão do braquete ocorre antes das expansões do paramater. Para obter o efeito que você espera, você precisa evalobter uma segunda rodada de expansões.
Glenn Jackman
@glennjackman Existe uma solução sem avaliação. Veja a segunda solução no final da minha resposta .

Respostas:

7

O shell se expande *apenas se não estiver entre aspas; qualquer aspeto interrompe a expansão pelo shell.

Além disso, uma expansão de colchete precisa ser sem 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 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: *.ext1e *.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.
Isso já foi explicado várias vezes antes para o uso de uma variável dentro de uma "expansão de chaves"

Para expandir uma "expansão de chave" que é o resultado de uma "expansão variável", é necessário reenviar a linha de comando para o shell com eval.

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

Chave -> Variável -> Glob || -> Brace -> Variável -> Glob
........ citado aqui -> ^^^^^^ || eval ^^^^^^^^^^^^^^^^^^^^^^^^^^

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 $listpoderia ser inseguro. No entanto, o valor de $listé definido pelo escritor do script. O gravador de script 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 avaliação) é usar o Bash "Extended Patterns" :

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

Nota: Esteja ciente de que ambas as soluções (avaliação e padrões) (conforme escritas) são seguras para nomes de arquivos com espaços ou novas linhas. Mas falhará em um $listcom espaços, porque $listnão está entre aspas ou o eval remove as aspas.

Comunidade
fonte
Sim, estendeu englobamento
glenn jackman
2

Considerar:

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

O problema é que a expansão do braquete é feita antes da expansão variável . Isso significa que, acima, a expansão da braçadeira nunca é realizada.

Isso ocorre porque, quando o bash vê pela primeira vez a linha de comando, não há chaves. Depois de secondListexpandido, é tarde demais.

O seguinte funcionará:

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

Aqui, a linha de comando possui chaves para que a expansão da chave possa ser executada como a primeira etapa. Depois disso, o valor de $sé substituído em ( expansão variável ) e, por fim, a expansão do nome do caminho é realizada.

Documentação

man bash explica a ordem da expansão:

A ordem das expansões é: expansão de chaves; expansão de til, expansão de parâmetro e variável, expansão aritmética e substituição de comando (feita da esquerda para a direita); divisão de palavras; e expansão do nome do caminho.

John1024
fonte