11

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.

Ryan
  • 423

4 Answers4

7

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.

200_success
  • 5,535
3

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"
  • doing rm $my_file means removing 'A' 'random' 'file.txt' which makes it three files.
  • doing rm "$my_file" will remove "A random file.txt" which makes one file.

Hope I've not confused you with these examples.

slm
  • 369,824
1

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:

-1

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.

polym
  • 10,852
mdpc
  • 6,834