Bash – When is {a,b,c} expanded in bash, when is it not

bashbrace-expansion

A bash script that contains

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

prints

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

when executed. This is what I expected – as the {a,b} construct is expanded.

However, when (another) script contains

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

it prints

{a,b}-{1,2}

which is not what I expected. I expected it to print a-1 a-2 b-1 b-2. Obviously, the {a,b} construct is not expanded.

I can make it expand like so

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

Based on these observations I have two questions: 1) when is the {a,b} construct expanded? 2) is $(echo {a,b}-{1,2}) the preferred way to trigger an expansion when required?

Best Answer

The Bash manual says that:

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.

Brace expansion is not in the list, so it isn't performed for the assignment v={a,b}-{1,2}. As mentioned by @Wildcard, the simple expansion to v=a-1 v=b-1 ... would be senseless anyway.

Also, when executing the echo $v, the following applies:

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.

Brace expansion happens before variable expansion, so the braces assigned to $v aren't expanded.

But you can do stuff like this:

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

Expanding it with $(echo ...) should work if you don't have any whitespace in the string to be expanded, and hence won't run into problems with word splitting. A better way might be to use an array variable if you can.

e.g. save the expansion into an array and run some command with the expanded values:

$ v=( {a,b}-{1,2} )
$ some_command "${v[@]}"
Related Question