In a shell script, if I define a variable such as FOO=25
, is there a difference between referencing it as $FOO$
and ${FOO}$
?
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
.
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:
-
I didn't want to edit your otherwise excellent answer, but shouldn't
$ var="foo *"
be$ foo="foo *"
? You don't usevar
anywhere. – Joe Oct 17 '15 at 13:41 -
@Joe oh, good grief! Yes, of course it should, thanks for pointing it out. Feel free to fix such mistakes by suggesting an edit, by the way. That sort of thing is very much encouraged, precisely to avoid silly errors like this one. – terdon Oct 17 '15 at 13:43
$
in the end! It will be treated as normal character and not as part of the variable name. – Byte Commander♦ Oct 9 '15 at 11:15