Referencing variables in a shell script
In a shell script, if I define a variable such as FOO=25
, is there a difference between referencing it as $FOO$
and ${FOO}$
?
Solution 1:
In most situations both $var
and ${var}
are the same. (Note that you must not use a $
at the end!)
One example where you need curly braces are when you need to put a variable in a continuous string.
Example:
var=hel
echo ${var}lo
will output hello
.
Solution 2:
To answer your main question, ${foo}
is called "parameter expansion". To be precise, the $
itself starts parameter expansion, the { }
are actually optional according to the POSIX specification, but can be useful to indicate the end of the variable's name:
$ foo="bar"
$ echo $fooBaz ## fails, no variable named $fooBaz exists
$ echo ${foo}Baz ## works, $foo is expanded and the string Baz is appended
barBaz
Basically, $foo
and ${foo}
are identical. Apart from cases like the above or when you are doing string manipulation, they are completely equivalent.
However, you shouldn't really use either of them. The rule of thumb is that, with very few exceptions, you should always use "$foo"
or "${foo}"
and never $foo
or ${foo}
. You should always quote your variables to avoid invoking the split+glob operator (more on that later). You certainly don't want $foo$
. The final $
is irrelevant:
$ foo="bar"
$ echo "$foo$"
bar$
So, while unquoted variables are sometimes OK:
$ echo $foo
bar
They are usually not and should really be avoided:
$ if [ -n $foo ]; then echo empty; else echo "not empty"; fi ## fails
empty
$ if [ -n "$foo" ]; then echo empty; else echo "not empty"; fi ## works
not empty
Note that the brackets do not help here either:
$ if [ -n ${foo} ]; then echo empty; else echo "not empty"; fi ## fails
empty
$ if [ -n "${foo}" ]; then echo empty; else echo "not empty"; fi ## works
not empty
When you use $foo
or ${foo}
, the shell will split the value saved in the variable on whitespace (this can be changed by setting the IFS
variable to something else) into a list and then, each element of the list is treated as a glob pattern and expanded into any matching files or directories. This is known as the split+glob operator. To illustrate, consider a directory with two files:
$ ls -l
-rw-r--r-- 1 terdon terdon 0 Oct 9 18:16 file1
-rw-r--r-- 1 terdon terdon 0 Oct 9 18:16 file2
Now, let's set a variable to foo *
:
$ foo="foo *"
What happens if we try to check if a file with that name exists?
$ if [ -e $foo ]; then echo "file exists"; else echo "no such file"; fi
file exists
The variable was split into foo
and *
and, since *
is a wildcard that matches any string, the shell tells you that a file called foo *
exxists. However, if we quote it correctly, this doesn't happen:
$ if [ -e "$foo" ]; then echo "file exists"; else echo "no such file"; fi
no such file
That was a trivial example to illustrate the point. Imagine if I had used rm
instead of echo
though.
So, first rule: always quote your variables. You can use either "$foo"
or "${foo}"
but quote it either way. For more detail on using variables safely, have a look at these posts:
- $VAR vs ${VAR} and to quote or not to quote
- Expansion of a shell variable and effect of glob and split on it
- Security implications of forgetting to quote a variable in bash/POSIX shells
- Why do I need to quote variable for if, but not for echo?