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

13

Um script bash que contém

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

impressões

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

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

No entanto, quando (outro) script contém

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

imprime

{a,b}-{1,2}

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

Eu posso fazê-lo expandir assim

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

Com base nessas observações, tenho duas perguntas: 1) quando o {a,b}construto é expandido? 2) é $(echo {a,b}-{1,2})a maneira preferida de desencadear uma expansão quando necessário?

René Nyffenegger
fonte
1
Isso é, pelo menos, consistente com o fato de que você não pode tornar uma variável de matriz simples =. Por exemplo, v=a-1 a-2não vai funcionar.
grochmal
@ grochmal - é porque você está atribuindo um valor escalar. v=a-1 a-2significa assign 'a-1' to variable v and run 'a-2' v=(a-1 a-2)atribui a matriz à variável v. v+=(b-1 b-2)anexa a ele.
6286

Respostas:

15

O manual do 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 chaves não está na lista e, portanto, não é realizada para a atribuição v={a,b}-{1,2}. Conforme mencionado pelo @Wildcard, a simples expansão 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 chaves ocorre antes da expansão variável, portanto, as chaves atribuídas a $vnã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 $(echo ...)deve funcionar se você não tiver nenhum espaço em branco na string a ser expandida e, portanto, não terá problemas com a divisão de palavras. Uma maneira melhor pode ser usar uma variável de matriz, se puder.

por exemplo, salve a expansão em uma matriz e execute algum comando com os valores expandidos:

$ v=( {a,b}-{1,2} )
$ some_command "${v[@]}"
ilkkachu
fonte
5

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

Uma variável pode ser atribuída por uma declaração do formulário

      nome = [ valor ]

Se o valor não for fornecido, a variável receberá uma sequência nula. Todos os valores passam por expansão de til, expansão de parâmetro e variável, substituição de comando, expansão aritmética e remoção de cotação (consulte EXPANSÃO abaixo).

Observe que a expansão do suporte NÃO é mencionada na lista.

No entanto, isso ainda deixa uma pergunta, a saber: como o shell sabe que essa é uma atribuição variável e, portanto, não está sujeita à expansão de chaves? Ou, mais precisamente, onde a sequência de análise é esclarecida e codificada de modo que o shell seja definido para identificar atribuições de variáveis ​​antes de lidar com a expansão de chaves? (Obviamente, é assim que bashfunciona, mas não encontrei a linha exata de documentação que descreve isso.)

Curinga
fonte
1
Talvez isso ? "2. As palavras que não são atribuições ou redirecionamentos de variáveis ​​são expandidas (consulte Expansões do Shell)."
Jeff Schaller
@JeffSchaller, você está correto. Na verdade, é melhor responder a essa pergunta apenas lendo a seção "EXPANSÃO DE COMANDO SIMPLES" ( LESS=+/SIMPLE man bash).
Curinga
0

de acordo com meu conhecimento, o {a, b, c} é expandido quando ecoado diretamente ou usado com um comando, por exemplo: mkdir ~ / {a, b, c}, mas quando definido como uma variável, deve ser avaliado antes ecoando ou usando-o 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

como você tem "a" seguido de "b" na ordem alfabética [az] e "1" seguido de "2" na ordem dec [0-9]: você pode usar o período duplo ".." com vírgula ", "

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
Jonah
fonte
1
A questão é por que vnão foi definida como a string literal "a-1 a-2 b-1 b-2". Ou pelo menos por que nenhum erro foi gerado se você digitar o comando: v=a-1 v=a-2 v=b-1 v=b-2(no qual você esperaria que a atribuição de variável fosse expandida). É uma boa pergunta; isso realmente não responde.
Curinga
@SatoKatsura, o que significa "expansão de curinga"? Há expansão de parâmetros, expansão de nome de caminho e expansão de chaves - qualquer uma das quais você pode estar se referindo e todas são diferentes. Ou você quis dizer divisão de palavras?
Curinga
0

A atribuição 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 de ilkkachu.

Expressões são expandidas quando são avaliadas.

Como isso:

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

Ou assim:

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

Ou assim:

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

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

Alexander
fonte