6

Consider the following snippet being entered at the command line in bash:

$ echo $(( $(wc -l letter.txt | cut -c1-4)/66 + 1)) 

Supposing letter.txt exists and has 66*4 lines, then this puts the string 5 on the command line. Now what I cannot follow is how this conclusion follows based on what I know about command line processing.

My book (Sobell's A Practical Guide to Linux, 4e) says

The Bourne Again Shell scans each token for the various types of expansion and sub- stitution in the following order. Most of these processes expand a word into a single word. Only brace expansion, word splitting, and pathname expansion can change the number of words in a command (except for the expansion of the variable "$@"— see page 474).

  1. Brace expansion (next page)
  2. Tilde expansion (page 368)
  3. Parameter and variable expansion (page 368)
  4. Arithmetic expansion (page 369)
  5. Command substitution (page 371)
  6. Word splitting (page 372)
  7. Pathname expansion (page 372)
  8. Process substitution (page 374)
  9. Quote removal (page 374)

Based on the above, it would seem that the arithmetic expansion would evaluate first and, if so, the token $(wc -l letter.txt | cut -c1-4) is not a valid operand for the integer arithmetic which bash deals with. What is my way out here/what am I not understanding?

4
  • 2
    That listed order also implies that filenames that resulted from globs would be parsed for process substitutions, i.e. that running echo * in a directory containing a file called <(rm -rf ~/*) would delete the contents of your home directory. Luckily, that's not how it works. I can't imagine how they've figured that order would make any sense. Commented Feb 12, 2024 at 21:04
  • I see what you mean now, thanks for pointing that out! Where does process substitution fit in the hierarchy then? It doesn't seem to be mentioned at all in the answer I accepted below. @ilkkachu Commented Feb 12, 2024 at 23:17
  • Right, I thought there was a link to the reference manual here already. It says "On systems that can support it, there is an additional expansion available: process substitution. This is performed at the same time as tilde, parameter, variable, and arithmetic expansion and command substitution." Commented Feb 13, 2024 at 9:46
  • Superb! Thanks again, @ilkkachu . I've marked this down as an error in my book. Commented Feb 13, 2024 at 17:58

1 Answer 1

13

Your book is not exactly correct. See man bash for the exact 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.

Note the usage of commas and semicolons. The parameter, variable, and arithmetic expansions plus the command substitution all happen with the same order.

The manual says the following about arithmetic expansion:

All tokens in the expression undergo parameter and variable expansion, command substitution, and quote removal.

And command substitution is described as

... executing command in a subshell environment

which means all expansions take place when nested from inside out.

$ echo $((1 + $(echo $((2+$(printf %s 3)))))) 6 
2
  • Fantastic, thank you so much! This book has been really good so far so I'm a bit surprised it would make what seemingly appears to be an error. Thanks for setting me straight. Commented Feb 12, 2024 at 19:54
  • I'm not surprised at all. Accurate shell documentation not written by either a standards body actively maintaining a standard to which shells conform or someone who is personally involved in implementing a major shell is vanishingly rare; published books are no exception. Commented Feb 14, 2024 at 0:42

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.