You need to use "$(somecmd "$file")"
.
Without the quotes, a path with a space will be split in the argument to somecmd
, and it will target the wrong file. So you need quotes on the inside.
Any spaces in the output of somecmd
will also cause splitting, so you need quotes on the outside of the whole command substitution.
Quotes inside the command substitution have no effect on the quotes outside of it. Bash's own reference manual isn't too clear on this, but BashGuide explicitly mentions it. The text in POSIX also requires it, as "any valid shell script" is allowed inside $(...)
:
With the $(command)
form, all characters following the open parenthesis to the matching closing parenthesis constitute the command. Any valid shell script can be used for command, except a script consisting solely of redirections which produces unspecified results.
Example:
$ file="./space here/foo"
a. No quotes, dirname
processes both ./space
and here/foo
:
$ printf "<%s>\n" $(dirname $file)
<.>
<here>
b. Quotes inside, dirname
processes ./space here/foo
, giving ./space here
, which is split in two:
$ printf "<%s>\n" $(dirname "$file")
<./space>
<here>
c. Quotes outside, dirname
processes both ./space
and here/foo
, outputs on separate lines, but now the two lines form a single argument:
$ printf "<%s>\n" "$(dirname $file)"
<.
here>
d. Quotes both inside and outside, this gives the correct answer:
$ printf "<%s>\n" "$(dirname "$file")"
<./space here>
(that would possibly have been simpler if dirname
only processed the first argument, but that wouldn't show the difference between cases a and c.)
Note that with dirname
(and possibly others) you also need want to add --
, to prevent the filename from being taken as an option in case it happens to start with a dash, so use "$(dirname -- "$file")"
.
"$(dirname "$0")"/stop.bash
? It seems to work... What's the story? – May 15 '18 at 18:22shell_level_1 $(shell_level_2) shell_level_1
: when you are inside the$(....)
you enter a "sublevel" of shell, BUT you can write in it as if you were on primary level (ie, you can directly write"
instead of\"
, etc). ex:touch "/tmp/a file" ; echo "its size is: $(find "/tmp/a file" -ls | awk '{print $5}) ..." : if you used backticks you'd have to
find "/tmp/a file"and
print $5. With
$(...)` no need: the shell adapts to the new level and you can write directly as if your interpreter now is at that level too. – Olivier Dulac May 16 '18 at 14:38