Wrt. $(((i=18)))
, you're right, it could be either $(( (i=18) ))
, or $( ((i=18)) )
, both are valid. A somewhat common (and definitely simple) way to interpret ambiguous cases like this is recognize the longest valid operator. That would mean this would interpreted as $((
followed by (
.
That's what happens with e.g. <<(
in Bash and Ksh: it's <<
followed by (
, and not <
followed by <(
. Even though the first interpretation is a syntax error while the second would be valid! The user must add a space to help the shell figure it out. In the same way, i+++a
is i++ + a
and not i + ++a
. Similar things exist in other languages, too.
But with $(((
, that's not exactly the whole truth. Some shells do look further, some don't. Consider e.g. this:
echo $((echo hi); (echo ho))
If interpreted as a command substitution, it's valid, and prints hi ho
. But greedily recognizing the $((
would have it interpreted as an arithmetic expansion, and as such, it's completely bogus.
Apart from Dash and Busybox, all shells I tried recognize the valid command substitution there. Putting a space between the first two parenthesis makes it unambigous.
As a matter of fact, it looks command substitutions come before arithmetic expansions
No, they happen at the same point of processing. To see that, create a command substitution that expands to what would be a valid arithmetic expansion. E.g. echo '$((1+2))'
prints $((1+2))
; so $( echo '$((1+2))' )
expands to $((1+2))
. But that's not further processed within the same command.
This,
echo $( echo '$((1+2))' )
outputs $((1+2))
and not 3
.
Of course an arithmetic expansion can only produce numbers, so the opposite order can't be tested. But similar experiments can be made between variable/parameter expansion and command substitution, and in none of the cases, the results of one expansion expand further.
Brace expansion, on the other hand is different.
Bash processes it before variable expansions:
$ bash -c 'v=000 va=123 vb=456; echo $v{a,b}; n=1 m=4; echo {$n..$m}'
123 456
{1..4}
while Ksh does the opposite:
$ ksh -c 'v=000 va=123 vb=456; echo $v{a,b}; n=1 m=4; echo {$n..$m}'
000a 000b
1 2 3 4
(In Ksh, even a="{1.."; b="4}"; echo $a$b
expands the brace, and outputs 1 2 3 4
. Zsh is again the sane one here, it expands variables first, but doesn't let expanded braces trigger further expansion. Zsh also recognizes <<(
as < <(
.)
And then of course, there's word splitting and filename generation, which happen after all others, but only for results of unquoted expansions.
$(((i=18)))
is equivalent to$((i=18))
. But now imagine$(((10-9)*2))
vs$((10-9*2))
– jesse_b Apr 13 '21 at 16:16$((
is arithmetic expansion and not command substitution – jesse_b Apr 13 '21 at 16:41echo $(( 19 + i=9 ))
. It gives 9. Can you please explain why? P.S.((i=18))
is a command – diciotto Apr 13 '21 at 16:55$
is followed by 1 or 2 opening parentheses. It very well may be but that is really easy logic to implement. – jesse_b Apr 13 '21 at 17:00