What is the difference between these two Bash if-statements? e.g.
if [ "$FOO" = "true" ]; then
vs
if [ $FOO = "true" ]; then
What is the difference? It seems that both statements work the same.
What is the difference between these two Bash if-statements? e.g.
if [ "$FOO" = "true" ]; then
vs
if [ $FOO = "true" ]; then
What is the difference? It seems that both statements work the same.
If the value of $FOO
is a single word that doesn't contain a wildcard character \[*?
, then the two are identical.
If $FOO
is unassigned, or empty, or more than one word (i.e., contains whitespace or $IFS
), then the unquoted version is a syntax error. If it happens to be just the right sequence of words (such as 0 -eq 0 -o false
), the result could be arbitrary. Therefore, it is good practice to always quote variables in shell scripts.
Incidentally, "true"
does not need to be quoted.
To illustrate what problems it might cause, here a few examples.
Let's say we have two variables as follow:
FOO="some value"
BAR="some value"
Now we have two variables holding exactly the same string/value. If we did some if statements to test the result, in your case:
if [ $FOO = "$BAR" ]; then echo "match"; else echo "no match"; fi
At this point you will get bash: [: too many arguments
. The unquoted $FOO holds now three values, namely '[ , some , value'
. [
test keyword doesn't know what to execute because it is expecting the first or second argument to be an operator.
When we quote "$FOO" we explicitly tell if
to look at the right values where no word splitting takes place.
Another example:
my_file="A random file.txt"
rm $my_file
means removing 'A' 'random' 'file.txt' which makes it three files.rm "$my_file"
will remove "A random file.txt" which makes one file. Hope I've not confused you with these examples.
Here, it's not that much about quoting or not in if
statements but in arguments to any command, and in that particular case, the [
command.
The [
command is a command like any other (though it's builtin in most Bourne-like shells) and like any other command can be used in the if
, then
, elif
, else
parts of if...fi
statements as well as outside if...fi
statements.
In Bourne-like shells other than zsh
, leaving a parameter expansion unquoted is the split+glob operator.
$FOO
, will be split on characters of $IFS
using complex rules and the resulting words subject to filename generation which will generate a variable number of arguments to the [
command where it expects exactly one.
So like in any argument to any command, that $FOO
must be quoted so its contents be passed as one argument.
[
is one of those commands where it is critical not to forget to quote variables, as not doing it amounts to an arbitrary command injections in many [
implementations (if there's a chance the contents of the variable may end up being under the control of an attacker).
That includes the [
builtin utility of bash
, ksh88
, ksh93
, pdksh
and derivatives (and zsh
when in sh
/ksh
emulation), and the [
standalone utility (/bin/[
, /usr/xpg4/bin/[
...) of those systems like Solaris where it's implemented as a wrapper script around ksh
's builtin [
.
Those [
implementations like all those builtin commands in those shells which under some circumstance can interpret arithmetic expressions or can operate on variable names can end up executing code.
Here, as an example, in the bash
shell (the shell of the GNU project which is also the sh
implementation of a few systems), and with the default value of $IFS
:
bash-5.0$ FOO='-v x[$(reboot)] -o true'
bash-5.0$ if [ $FOO = "true" ]; then echo yes; fi
System rebooting now!
yes
Because it was not quoted and because $IFS
contains the SPC character by default, $FOO
was split into -v
, x[$(reboot)]
, -o
and true
. Of those, only the second contains glob operators ([...]
). In my case, it didn't match any file (that would be different if there was a xo
file in the current directory for instance), so was left as is (another peculiarity of Bourne-like shells, most other shells would complain that the glob didn't match).
So that ended up asking [
whether the $x
array was set for indice $(reboot)
, and ended up running reboot
as part of the evaluation of that arithmetic expression, or (-o
) whether true
was equal to true
, hence the yes
output.
More reading at:
In this specific case there is no difference.
However, if $FOO
contains a space or some special characters, you will have a problem.
In the "$FOO"
case, it will use the variable in total, to make the match insulating you from the space problem.
However, if you use $FOO
and there is a special case it will affect the if statement.
a='foo bar'; [[ $a == "foo bar" ]]
. However, a variable which does not contain glob characters need not be:[[ $a == $a ]]
. Word expansion is not performed inside double square brackets. And for regex matching, the pattern on the right hand side must not be quoted or it will be taken as literal string:[[ $a =~ .*oo.*r ]]
(the pattern should be in an un-quoted variable, however, instead ... – Dennis Williamson Aug 15 '13 at 01:14[[ $a == foo* ]]
. Can you provide any additional examples of a requirement for quoting besides my literal string example? – Dennis Williamson Aug 15 '13 at 01:14