0

Imagine I want to call some command some-command via $() with an argument stored in another variable argument, the latter containing space.

  1. With my current understanding, the fact that result="$(some-command $argument)" (e.g. expansion) leads to passing two arguments is as expected.

  2. Question part: why does the result="$(some-command "$argument")" (e.g. concatenation) lead to the desired result of passing one single argument?

More details:

./some-command:

#!/usr/bin/env bash
echo "Arg 1: $1"
echo "Arg 2: $2"

./test-script:

#!/usr/bin/env bash

export PATH="`pwd -P`:$PATH"

argument="one two"

echo "Calling with expansion"
res="$(some-command $argument)"
echo $res

echo "Calling with concatenation"
res="$(some-command "$argument")"
echo $res

Calling test-script leads to the following output:

Calling with expansion
Arg 1: one Arg 2: two
Calling with concatenation
Arg 1: one two Arg 2:

I seem to not grasp when smth is expanded / evaluated and how the expanded variables are grouped into arguments passed to scripts.

Thank you!

P.S. Bonus curiosity is why result="$(some-command \"$argument\")" does not work at all.

agronskiy
  • 300
  • 4
  • 11
  • 1
    Do you have a space after `some-command` or not? – choroba Mar 02 '21 at 17:05
  • Good catch, inactual scripts space is there, corrected! – agronskiy Mar 02 '21 at 17:07
  • [BashFAQ #50](http://mywiki.wooledge.org/BashFAQ/050) is your friend -- it explains why argument lists should __never__ be stored as strings, and what to use instead (short form: either arrays or functions, depending on the context). – Charles Duffy Mar 02 '21 at 17:19
  • ...as for `result="$(some-command \"$argument\")"`, if `argument="one two"`, it's running `some-command '"one' 'two"'` – Charles Duffy Mar 02 '21 at 17:27

1 Answers1

1

That's how quoting and expansions work in bash. In fact, double quotes after = are not needed, as word-splitting is not performed, so

result=$(some-command "$argument")

should work the same way.

There's no "concatenation" going on. Bash treats the string inside $() as a command and runs all the expansions on it before running it.

So, what happens with some-command "$argument"? First, the parameter expansion expands $argument into a string containing spaces. When word-splitting happens, it notes the string was enclosed in double quotes, so it keeps it as a single string.

choroba
  • 231,213
  • 25
  • 204
  • 289
  • I see, thanks! So in this case the relation between the outer pair of quotes and the inner one is not the same as in say `res="literal1"$stringvar2"literal3"`? I.e. the quoted part is not “switched off/on” but bash rather recognizes those as quotes inside quotes? – agronskiy Mar 02 '21 at 17:35
  • 1
    @agronskiy: Exactly. – choroba Mar 02 '21 at 17:43
  • Thank you! Should have had closer look to the dup question... – agronskiy Mar 02 '21 at 17:51