In my Bash environment I use variables containing spaces, and I use these variables within command substitution.
What is the correct way to quote my variables? And how should I do it if these are nested?
DIRNAME=$(dirname "$FILE")
or do I quote outside the substitution?
DIRNAME="$(dirname $FILE)"
or both?
DIRNAME="$(dirname "$FILE")"
or do I use back-ticks?
DIRNAME=`dirname "$FILE"`
What is the right way to do this? And how can I easily check if the quotes are set right?
Best Answer
In order from worst to best:
DIRNAME="$(dirname $FILE)"
will not do what you want if$FILE
contains whitespace (or whatever characters$IFS
currently contains) or globbing characters\[?*
.DIRNAME=`dirname "$FILE"`
is technically correct, but backticks are not recommended for command expansion because of the extra complexity when nesting them and the extra backslash processing that happens within them.DIRNAME=$(dirname "$FILE")
is correct, but only because this is an assignment to a scalar (not array) variable. If you use the command substitution in any other context, such asexport DIRNAME=$(dirname "$FILE")
ordu $(dirname -- "$FILE")
, the lack of quotes will cause trouble if the result of the expansion contain whitespace or globbing characters.DIRNAME="$(dirname "$FILE")"
(except for the missing--
, see below) is the recommended way. You can replaceDIRNAME=
with a command and a space without changing anything else, anddirname
receives the correct string.To improve even further:
DIRNAME="$(dirname -- "$FILE")"
works if$FILE
starts with a dash.DIRNAME="$(dirname -- "$FILE" && printf x)" && DIRNAME="${DIRNAME%?x}" || exit
works even if$FILE
's dirname ends with a newline, since$()
chops off newlines at the end of output, both the one added bydirname
and the ones that may be part of the actual data.You can nest command expansions as much as you like. With
$()
you always create a new quoting context, so you can do things like this:You do not want to try that with backticks.