A common trope on StackOverflow bash is: "Why doesn't x=99; echo {1..$x}
work?"
The answer is "because braces are expanded before parameters/variables".
Therefore, I thought it should be possible to expand multiple variables using a single $
and a brace. I'd expect a=1; b=2; c=3; echo ${{a..c}}
to print 1 2 3
. First, the inner brace would expand to ${a} ${b} ${c}
(which it does when writing echo \${{a..c}}
). Then that result would undergo parameter expansion.
However, I got -bash: ${{a..c}}: bad substitution
so {a..c}
wasn't expanded at all.
Bash's manual is a bit more specific (emphasis mine).
Expansion is performed on the command line after it has been split into tokens [...] 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 filename expansion.
Note the ;
and ,
in that list. "Left-to-right fashion" seems to apply to the whole (therefore unordered) list before the ;
. Just like the mathematical operators *
and /
have no precedence over each other.
Ok, so brace expansion is not really of higher precedence than parameter expansion. It's just that both {1..$x}
and ${{a..c}}
are evaluated from left to right, meaning the brace {
comes before the parameter $x
and the parameter ${
comes before the brace {a..c}
.
Or so I thought. However, when using $
instead of ${
then parameters on the left expand after braces on the right:
# in bash 5.0.3(1)
x=nil; x1=one; x2=two
echo ${x{1..2}} # prints `-bash: ${x{1..2}}: bad substitution`
echo $x{1..2} # prints `one two`
I'm just asking because I'm curious. I don't plan to use thinks like $x{1..2}
anywhere. I'm not interested in better solutions or alternatives to address multiple variables (e.g. array slices ${array[@]:1:2}
). I just want to get a deeper understanding.
from: https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html
To avoid conflicts with parameter expansion, the string ‘${’ is not considered eligible for brace expansion, and inhibits brace expansion until the closing ‘}’.
That said, for echo $x{1..2}
, first the brace expansion takes place, and then the parameter expansion, so we have echo $x1 $x2
. For echo ${x{1..2}}
the brace expansion doesn't happen, because we are after the ${
and haven't reached the closing }
of the parameter expansion.
Regarding the bash manual part you have quoted, left-to-right order still exists for the expansions (with respect to allowed nested ones). Things get clearer if you format the list instead of using ,
and ;
: