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

8

Estou tentando expandir uma string envolvendo um caractere curinga e uma coleção de extensões especificadas entre chaves. Nada parece funcionar como o exemplo abaixo ilustra. a variável firstList se expande bem, mas nem secondList , thirdList ou fourthList se expande adequadamente. Eu também tentei várias versões do eval mas nenhum trabalho também. 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"
    
por Leo Simon 19.05.2016 / 23:28

2 respostas

5

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.

    
por 21.05.2016 / 05:48
1

Considere:

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

O problema é que expansão da chave é feita antes expansão da variável . Isso significa que, no acima, a expansão de contraventamento nunca é executada.

Isso porque, quando o bash primeiro vê a linha de comando, não há chaves. Depois que secondList for expandido, é tarde demais.

Os seguintes itens funcionarão:

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

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

Documentação

man bash explica a ordem de expansão:

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.

    
por 20.05.2016 / 05:00