Posso usar variáveis dentro da expansão {} sem 'eval'?

7

Posso usar variáveis dentro da expansão {} sem apelar para eval ? Se sim, como?

Isso não funciona:

$ touch 1.foo 1.bar
$ ls 1.{foo,bar}
1.bar  1.foo
$ extensions=foo,bar
$ ls 1.{$extensions}
ls: cannot access 1.{foo,bar}: No such file or directory

Funciona com eval :

$ eval ls 1.{$extensions}
1.bar  1.foo
    
por n.r. 27.02.2014 / 00:05

3 respostas

9

A expansão do recurso acontece muito cedo durante a expansão (primeira coisa, na verdade ), antes da expansão da variável. Para executar a expansão de contraventamento no resultado de uma expansão de variável, você precisa usar eval .

Você pode obter o mesmo efeito sem eval se você tornar extensions um padrão de curinga em vez de um padrão de chave. Defina a opção extglob para ativar padrões semelhantes ao ksh .

shopt -s extglob
extensions='@(foo|bar)'
ls 1.$extensions
    
por 27.02.2014 / 02:51
3

Aqui está uma maneira de expandir variáveis dentro de chaves sem eval :

end=3
declare -a 'range=({'"1..$end"'})'

Agora temos uma boa variedade de números:

for i in ${range[@]};do echo $i;done
1
2
3

Testado no bash 4.3.11, mas deve funcionar em todas as versões modernas.

    
por 22.01.2015 / 12:54
2

Outra técnica que você pode usar no bash e em algumas outras shells é usar uma matriz:

$ extensions=(foo bar)
$ ls "${extensions[@]/#/1.}"

Este é o %código% (substituição de padrão) forma de expansão de parâmetro. (Infelizmente, isso também não é compatível com POSIX). O ${parameter/pattern/string} especifica que a substituição deve ser aplicada para cada elemento da matriz, e que a separação entre os elementos deve ser mantida. [@] em uma string padrão age como # em uma expressão regular (isto é, a maioria das substituições ^ ); isso significa que a substituição deve ocorrer somente no início dos valores dos parâmetros. Então

$ ls "${extensions[@]/#/1.}"

é equivalente a

$ ls "${extensions[0]/#/1.}" "${extensions[1]/#/1.}"

(uma vez que este array tem dois elementos, indexados como 0 e 1) e isso se expande para

$ ls "1.foo" "1.bar"

Citação

As citações são importantes se as "extensões" exigirem citações - ou seja, se eles (possivelmente, possivelmente) contiverem espaços ou expansão de nome de caminho (glob / curinga) caracteres. Por exemplo, se

$ extensions=("foo bar" "*r")

então

$ ls ${extensions[@]/#/1.}

(sem aspas) se expande para

$ ls 1.foo bar 1.*r

(em que s/old/new/ e 1.foo são argumentos separados), e isso, por sua vez, se expande para

$ ls 1.foo bar 1.anteater 1.bar 1.bear 1.cougar 1.deer 1.grasshopper …

(porque bar é um caractere curinga não indicado). Para mais discussão sobre a importância de citar, veja Implicações de segurança de esquecer de citar uma variável em shells bash / POSIX .

Você também pode combinar o final de uma string usando 1.*r no padrão :

$ fnames=(cat dog)
$ ls "${fnames[@]/%/.c}"

expande para

$ ls "cat.c" "dog.c"

Mas cuidado: % não funciona da mesma forma que % em expressões regulares. Você tem que colocá-lo no início do padrão para restringir a correspondência para ocorrer no final do valor do parâmetro. Por exemplo, se você tiver

$ fnames=(cat.c dog.c)

e você deseja obter $ e cat.o , > não faça dog.o - não funcionará. Fazer

$ ls "${fnames[@]/%.c/.o}"

Mas não "${fnames[@]/.c%/.o}" (deixando de fora o "${fnames[@]/.c/.o}" ) ou - se um dos “fnames” for % , ele será convertido em dog.catcher.c (desde que o primeiro dog.oatcher.c é substituído por .c ).

Infelizmente, não há uma maneira fácil de adicionar um prefixo e um sufixo, como você faria com

$ ls 1.{foo,bar}.c

Veja a documentação do bash para mais informações sobre variáveis, arrays, e as transformações que você pode aplicar a elas.

    
por 12.10.2016 / 20:26