1

In my understanding of command substitution this should work, but it doesn't, can you explain me why and how to do something like this.


Why does this work:

cd ..; echo 123            # output is "123", after changing directories

...when this doesn't:

cd $(echo "..; echo 123")  # error message is "cd: too many arguments"
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Salmjak
  • 57
  • 5
  • [BashFAQ #50](http://mywiki.wooledge.org/BashFAQ/050) is closely related to this question, insofar as both questions stem from the same misunderstanding. – Charles Duffy Dec 04 '19 at 20:48
  • 1
    Just as an intuitive idea of why things work the way they do, though: if you have a string `s='Could not run: echo >/etc/passwd'`, you wouldn't want the command `echo $s` to overwrite `/etc/passwd`, right? (Though `echo $s` [has other bugs](http://mywiki.wooledge.org/BashPitfalls#echo_.24foo); it should be `echo "$s"` or, even better, `printf '%s\n' "$s"` to be correct with arbitrary data). – Charles Duffy Dec 04 '19 at 20:52

1 Answers1

3

Command substitution results (like expansion of variables) do not go through all parsing phases; they only go through word-splitting[1] and glob expansion[2], and even those happen only when the expansion itself is unquoted.

That means that your result is identical to:

cd "..;" "echo" "123"

...the semicolon is treated as literal text to be passed to the cd command, not shell syntax.

This is a feature, not a bug: If command substitution results were parsed as syntax, writing secure shell scripts handling untrusted data would be basically impossible.


[1] dividing the results into "words" on characters in IFS -- or, by default, whitespace.
[2] looking at whether each resulting word can be treated as a filename-matching pattern, and, if so, matching them against the local filesystem.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441