1

The book “Learning the bash Shell” 3rd ed. by Cameron Newham and Bill Rosenblatt has at page 181 a picture describing the command-line processing of bash shell. That picture shows that Command substitution is performed before Arithmetic substitution (sic). The bash man page, though, reports a different order: “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.” Is the book wrong? If it is, can you please provide an example demonstrating it?

diciotto
  • 69
  • 3

2 Answers2

3

The official documentation is right (but mind the subtle difference between "," and ";" in there):

Command substitution $()/`` and arithmetic substitution $(()) have the same precedence. Whatever comes first when reading left to right, is expanded first.

You can confirm this with a test. Here, we use the built-in variable $SECONDS, which contains the time in seconds since bash started.

#! /bin/bash
echo "$(sleep 1; echo "command $SECONDS"; sleep 1), arithmetic $((SECONDS))"
echo "arithmetic $((SECONDS)), $(sleep 1; echo "command $SECONDS"; sleep 1)"

This prints

command 1, arithmetic 2
arithmetic 2, command 3
Socowi
  • 25,550
  • 3
  • 32
  • 54
  • For another example, try `export x=1; echo "cmd:$(printenv x), arith:$((++x)), cmd:$(printenv x), arith:$((++x)), cmd:$(printenv x)"` -- here, it's the arithmetic expansions changing the variable, and you can see the command substitutions being affected by the changes as it works through them from left to right. – Gordon Davisson Aug 05 '23 at 21:06
1

1st of all, according to the official bash documentation The order is as follows:

Expansion is performed on the command line after it has been split into tokens. There are seven kinds of expansion performed:

  1. brace expansion
  2. tilde expansion
  3. parameter and variable expansion
  4. command substitution
  5. arithmetic expansion
  6. word splitting
  7. filename expansion

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.

As @socowi points out in his answer, there is a difference between ; and , when it comes to the order of operations, so kinds 2 - 5 are done simultaneously, left to right.

So as you've suspected your book is wrong. Now, I initially suspected that if command substitution would happen after arithmetic expansion we could wind up with situations like:

uberhumus@homepc:~$ kuki=$(echo "if command substitution happens before arithmetic substitution, this $((2+3)) is 5 else it's 2+3") 
uberhumus@homepc:~$ echo $kuki 
if command substitution happens before arithmetic substitution, this $((2+3)) is 5 else it's $((2+3))

This was wrong. As (echo "if command substitution happens before arithmetic substitution, this $((2+3)) is 5 else it's 2+3") is a command on its own that means that the expansion order occurs within it too so that situation is impossible.

In fact if you run set -vx before the above experiment you can see that, as the innermost expansion, $((2+3)) is performed immediately.

uberhumus@homepc:~$ set -vx
uberhumus@homepc:~$ kuki=$(echo "if command substitution happens before arithmetic substitution, this $((2+3)) is 5 else it's 2+3")
kuki=$(echo "if command substitution happens before arithmetic substitution, this $((2+3)) is 5 else it's 2+3")
++ echo 'if command substitution happens before arithmetic substitution, this 5 is 5 else it'\''s 2+3'
+ kuki='if command substitution happens before arithmetic substitution, this 5 is 5 else it'\''s 2+3'

uberhumus@homepc:~$ echo $kuki 
echo $kuki 
+ echo if command substitution happens before arithmetic substitution, this 5 is 5 else 'it'\''s' 2+3
if command substitution happens before arithmetic substitution, this 5 is 5 else it's 2+3
Uberhumus
  • 921
  • 1
  • 13
  • 24