Quando é {a, b, c} expandido no bash, quando não é?

12

Um script bash que contém

for i in {a,b}-{1,2}; do
  echo $i;
done

imprime

a-1
a-2
b-1
b-2

quando executado. Isso é o que eu esperava - à medida que a construção {a,b} é expandida.

No entanto, quando (outro) script contém

v={a,b}-{1,2}
echo $v

imprime

{a,b}-{1,2}

que não é o que eu esperava. Eu esperava imprimir a-1 a-2 b-1 b-2 . Obviamente, a construção {a,b} não é expandida.

Eu posso fazer isso se expandir assim

v=$(echo {a,b}-{1,2})

Com base nessas observações, tenho duas perguntas: 1) quando a construção {a,b} é expandida? 2) é $(echo {a,b}-{1,2}) o caminho preferido para acionar uma expansão quando necessário?

    
por René Nyffenegger 05.07.2016 / 21:59

4 respostas

14

O Manual de Bash diz que:

SIMPLE COMMAND EXPANSION
When a simple command is executed, the shell performs the following
expansions, assignments, and redirections, from left to right.
[...]
4. The  text  after the = in each variable assignment undergoes tilde
   expansion, parameter expansion, command substitution, arithmetic
   expansion, and quote removal before being assigned to the variable.

A expansão de contraventamento não está na lista, por isso não é realizada para a atribuição v={a,b}-{1,2} . Como mencionado por @Wildcard, a expansão simples para v=a-1 v=b-1 ... seria sem sentido de qualquer maneira.

Além disso, ao executar o echo $v , o seguinte se aplica:

EXPANSION
Expansion is performed on the command line after it has been split
into words. [...]

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.

A expansão de contraventamento acontece antes da expansão da variável, portanto, as chaves atribuídas a $v não são expandidas.

Mas você pode fazer coisas assim:

$ var_this=foo var_that=bar
$ echo $var_{this,that}
foo bar

A expansão com $(echo ...) deve funcionar se você não tiver espaço em branco na string a ser expandida e, portanto, não terá problemas com a divisão de palavras. Uma maneira melhor seria usar uma variável array, se puder.

por exemplo. salve a expansão em um array e execute algum comando com os valores expandidos:

$ v=( {a,b}-{1,2} )
$ some_command "${v[@]}"
    
por 05.07.2016 / 22:28
5

Um ponto interessante. Possivelmente útil é o seguinte trecho de man bash :

A variable may be assigned to by a statement of the form

      name=[value]

If value is not given, the variable is assigned the null string.  All values undergo tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal (see EXPANSION below).

Observe que a expansão de contraventamento NÃO é mencionada na lista.

No entanto, isso ainda deixa uma pergunta, a saber: Como o shell sabe que isso é uma atribuição de variável e, portanto, não está sujeito à expansão de chaves? Ou, mais precisamente, onde a sequência de análise é esclarecida e codificada de tal forma que o shell é definido para identificar atribuições de variáveis antes de lidar com a expansão de chaves? (Isso é obviamente como bash funciona, mas não encontrei a linha exata de documentação descrevendo isso.)

    
por 05.07.2016 / 22:28
0

de acordo com meu conhecimento, o {a, b, c} é expandido quando é diretamente ecoado ou usado com um comando, por exemplo: mkdir ~ / {a, b, c}, mas quando é definido como uma variável deve ser avaliado antes de ecoar ou usá-lo como argumento!

u@h:~$ echo {a,b}-{1,2}
a-1 a-2 b-1 b-2
u@h:~$ v={a,b}-{1,2}
u@h:~$ echo $v
{a,b}-{1,2}
u@h:~$ eval echo $v
a-1 a-2 b-1 b-2

desde que você tenha "a" seguido por "b" na ordem alfabética [az], e "1" seguido por "2" na ordem de decaimento [0-9]: você pode usar o período duplo ".. "insteed de comma", "

u@h:~$ echo {a..b}-{1..2}
a-1 a-2 b-1 b-2
u@h:~$ v={a..b}-{1..2}
u@h:~$ echo $v
{a..b}-{1..2}
u@h:~$ eval echo $v
a-1 a-2 b-1 b-2
    
por 05.07.2016 / 22:09
0

Atribuir a uma variável no bash não expande a expressão.

Para este pequeno script, x conterá "*" e não a expansão de "*" :

#!/bin/bash
x=*
echo "$x"

No entanto, alguns valores são expandidos, ref. a boa resposta do ilkkachu.

As expressões são expandidas quando são avaliadas.

Assim:

x=$(echo *)        # <-- evaluation of "*"
echo "$x"

Ou assim:

x=*
echo $x            # <-- evaluation of $x

Ou assim:

x=*
eval echo "$x"     # <-- evaluation of 'echo *'

Triggering $() é bastante comum e eu acho que é preferível a eval , mas o melhor provavelmente não é disparar a avaliação, até que a variável seja realmente usada em um comando.

    
por 05.07.2016 / 23:16