136

I can write

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"

the end result to me all seems about the same. Why should I write one or the other? are any of these not portable/POSIX?

99

VAR=$VAR1 is a simplified version of VAR=${VAR1}. There are things the second can do that the first can't, for instance reference an array index (not portable) or remove a substring (POSIX-portable). See the More on variables section of the Bash Guide for Beginners and Parameter Expansion in the POSIX spec.

Using quotes around a variable as in rm -- "$VAR1" or rm -- "${VAR}" is a good idea. This makes the contents of the variable an atomic unit. If the variable value contains blanks (well, characters in the $IFS special variable, blanks by default) or globbing characters and you don't quote it, then each word is considered for filename generation (globbing) whose expansion makes as many arguments to whatever you're doing.

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

On portability: According to POSIX.1-2008 section 2.6.2, the curly braces are optional.

  • @shawn updated my question because I'm also curious about portability – xenoterracide Dec 16 '10 at 13:46
  • @shawn: I doubt your example is valid. Do you have any real example of a shell where var1=$var expansion gives an error? – alex Dec 16 '10 at 14:48
  • @alex: Thanks. I thought I had tested that on the command line, but I did it wrong. I changed the example. – Shawn J. Goff Dec 16 '10 at 15:19
  • With the updated example it's better to keep in mind that you generally should want the quoted version, as the example is rather a corner case. – alex Dec 16 '10 at 16:15
  • 9
    @Shawn: The quotes are not necessary in an assignment. They are necessary in most other uses, including export VAR=$VAR1. As for the braces, they are optional (check the fourth paragraph of the section you cited; this is the case in all pre-POSIX and POSIX shells). – Gilles Dec 16 '10 at 19:13
59

${VAR} and $VAR are exactly equivalent. For a plain variable expansion, the only reason to use ${VAR} is when parsing would otherwise grab too many characters into the variable name, as in ${VAR1}_$VAR2 (which without braces would be equivalent to ${VAR1_}$VAR2). Most adorned expansions (${VAR:=default}, ${VAR#prefix}, …) require braces.

In a variable assignment, field splitting (i.e. splitting at whitespace in the value) and pathname expansion (i.e. globbing) are turned off, so VAR=$VAR1 is exactly equivalent to VAR="$VAR1", in all POSIX shells and in all pre-POSIX sh that I've heard of. (POSIX ref: simple commands). For the same reason, VAR=* reliably sets VAR to the literal string *; of course VAR=a b sets VAR to a since the b is a separate word in the first place. Generally speaking, double quotes are unnecessary where the shell syntax expects a single word, for example in case … in (but not in the pattern), but even there you need to be careful: for example POSIX specifies that redirection targets (>$filename) don't require quoting in scripts, but a few shells including bash do require the double quotes even in scripts. See When is double-quoting necessary? for a more thorough analysis.

You do need the double quotes in other cases, in particular in export VAR="${VAR1}" (which can equivalently be written export "VAR=${VAR1}") in many shells (POSIX leaves this case open). The similarity of this case with simple assignments, and the scattered nature of the list of cases where you don't need double quotes, are why I recommend just using double quotes unless you do want to split and glob.

  • 2
    As a general rule, I will always quote variable expansions even when I know the value won't contain any IFS characters because I want to be in the habit. The one exception is I don't quote the value when doing a variable assignment (unless required, such as when the value contains a space). This makes editor syntax highlighting more useful when there are command substitutions such as FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Rather than coloring everything the "string" color, I get syntax highlighting of the nested code. This is also why I avoid backticks. – Richard Hansen Jan 4 '13 at 19:19
  • While >$file is OK in POSIX scripts, it is not in bash even when non-interactive (unless POSIX compliance is enforced with $POSIXLY_CORRECT or --posix...). – Stéphane Chazelas Jan 23 '13 at 15:01
  • While it's true that quotes aren't needed in VAR=$VAR1, I've been surprised sometimes by local VAR=$VAR1, which I remember working differently in some respects, in at least some shells. But atm, I can't reproduce the divergence. – dubiousjim Sep 19 '15 at 8:31
  • Ok, found the issue I was remembering. It only shows up in some shells. – dubiousjim Sep 19 '15 at 10:47
  • @dubiousjim local VAR=$VAR1 is like export VAR=$VAR1, it depends on the shell. – Gilles Sep 19 '15 at 13:17
6

Quotation

Consider that double-quote is used for variable expansion, and single-quote is used for strong-quoting, i.e., sans expansion.

Expansion:

this='foo'
that='bar'
these="$this"
those='$that'

Output:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

It might be worthwhile to mention that you should use quotation wherever possible for several reasons, among the best of which are that it's considered best practice, and for readability. Also because Bash is quirky at times and often for seemingly illogical or unreasonable/unexpected ways, and quotation changes implicit expectations to explicit, which reduces that error surface (or potential-for therein).

And while it's completely legal to not quote, and will work in most cases, that functionality is provided for convenience and is probably less portable. the fully-formal practice guaranteed to reflect intent and expectation is to quote.

Substitution

Now consider also that the construct "${somevar}" is used for substitution operations. Several use cases, such as replacement, and arrays.

Replacement (stripping):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

Replacement (replacement):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

Arrays:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

All of this is barely scratching the surface of the "${var}" substitution construct. The definitive reference for Bash shell scripting is the libre online reference, TLDP The Linux Documentation Project https://www.tldp.org/LDP/abs/html/parameter-substitution.html

0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

end then:

env=$1
    if [ ! -f /dirname/${env}hostname ]

worth mentioning as a clearer example of using curlies

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.