Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between ${var}, "$var", and "${var}" in the Bash shell?

People also ask

What is the difference between $VAR and $VAR in shell?

$var and ${var} are exactly equivalent. "$var" and "${var}" are exactly equivalent.

What is ${ var in bash?

Using the ${#VAR} syntax will calculate the number of characters in a variable. If VAR is "*" or "@", this value is substituted with the number of positional parameters or number of elements in an array in general.

What is the difference between $VAR and $$ VAR?

Difference between Both: The variable $var is used to store the value of the variable and the variable $$val is used to store the reference of the variable.

What is ${ var in Linux?

/var contains variable data files. This includes spool directories and files, administrative and logging data, and transient and temporary files. Some portions of /var are not shareable between different systems.


Braces ($var vs. ${var})

In most cases, $var and ${var} are the same:

var=foo
echo $var
# foo
echo ${var}
# foo

The braces are only needed to resolve ambiguity in expressions:

var=foo
echo $varbar
# Prints nothing because there is no variable 'varbar'
echo ${var}bar
# foobar

Quotes ($var vs. "$var" vs. "${var}")

When you add double quotes around a variable, you tell the shell to treat it as a single word, even if it contains whitespaces:

var="foo bar"
for i in "$var"; do # Expands to 'for i in "foo bar"; do...'
    echo $i         #   so only runs the loop once
done
# foo bar

Contrast that behavior with the following:

var="foo bar"
for i in $var; do # Expands to 'for i in foo bar; do...'
    echo $i       #   so runs the loop twice, once for each argument
done
# foo
# bar

As with $var vs. ${var}, the braces are only needed for disambiguation, for example:

var="foo bar"
for i in "$varbar"; do # Expands to 'for i in ""; do...' since there is no
    echo $i            #   variable named 'varbar', so loop runs once and
done                   #   prints nothing (actually "")

var="foo bar"
for i in "${var}bar"; do # Expands to 'for i in "foo barbar"; do...'
    echo $i              #   so runs the loop once
done
# foo barbar

Note that "${var}bar" in the second example above could also be written "${var}"bar, in which case you don't need the braces anymore, i.e. "$var"bar. However, if you have a lot of quotes in your string these alternative forms can get hard to read (and therefore hard to maintain). This page provides a good introduction to quoting in Bash.

Arrays ($var vs. $var[@] vs. ${var[@]})

Now for your array. According to the bash manual:

Referencing an array variable without a subscript is equivalent to referencing the array with a subscript of 0.

In other words, if you don't supply an index with [], you get the first element of the array:

foo=(a b c)
echo $foo
# a

Which is exactly the same as

foo=(a b c)
echo ${foo}
# a

To get all the elements of an array, you need to use @ as the index, e.g. ${foo[@]}. The braces are required with arrays because without them, the shell would expand the $foo part first, giving the first element of the array followed by a literal [@]:

foo=(a b c)
echo ${foo[@]}
# a b c
echo $foo[@]
# a[@]

This page is a good introduction to arrays in Bash.

Quotes revisited (${foo[@]} vs. "${foo[@]}")

You didn't ask about this but it's a subtle difference that's good to know about. If the elements in your array could contain whitespace, you need to use double quotes so that each element is treated as a separate "word:"

foo=("the first" "the second")
for i in "${foo[@]}"; do # Expands to 'for i in "the first" "the second"; do...'
    echo $i              #   so the loop runs twice
done
# the first
# the second

Contrast this with the behavior without double quotes:

foo=("the first" "the second")
for i in ${foo[@]}; do # Expands to 'for i in the first the second; do...'
    echo $i            #   so the loop runs four times!
done
# the
# first
# the
# second

TL;DR

All the examples you give are variations on Bash Shell Expansions. Expansions happen in a particular order, and some have specific use cases.

Braces as Token Delimiters

The ${var} syntax is primarily used for delimiting ambiguous tokens. For example, consider the following:

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo ${var1}2
foo2

Braces in Array Expansions

The braces are required to access the elements of an array and for other special expansions. For example:

$ foo=(1 2 3)

# Returns first element only.
$ echo $foo
1

# Returns all array elements.
$ echo ${foo[*]}
1 2 3

# Returns number of elements in array.
$ echo ${#foo[*]}
3

Tokenization

Most of the rest of your questions have to do with quoting, and how the shell tokenizes input. Consider the difference in how the shell performs word splitting in the following examples:

$ var1=foo; var2=bar; count_params () { echo $#; }

# Variables are interpolated into a single string.
$ count_params "$var1 $var2"
1

# Each variable is quoted separately, created two arguments.
$ count_params "$var1" "$var2"
2

The @ symbol interacts with quoting differently than *. Specifically:

  1. $@ "[e]xpands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word."
  2. In an array, "[i]f the word is double-quoted, ${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS variable, and ${name[@]} expands each element of name to a separate word."

You can see this in action as follows:

$ count_params () { echo $#; }
$ set -- foo bar baz 

$ count_params "$@"
3

$ count_params "$*"
1

The use of a quoted expansion matters a great deal when variables refer to values with spaces or special characters that might prevent the shell from word-splitting the way you intend. See Quoting for more on how quoting works in Bash.


You need to distinguish between arrays and simple variables — and your example is using an array.

For plain variables:

  • $var and ${var} are exactly equivalent.
  • "$var" and "${var}" are exactly equivalent.

However, the two pairs are not 100% identical in all cases. Consider the output below:

$ var="  abc  def  "
$ printf "X%sX\n" $var
XabcX
XdefX
$ printf "X%sX\n" "${var}"
X  abc  def  X
$

Without the double quotes around the variable, the internal spacing is lost and the expansion is treated as two arguments to the printf command. With the double quotes around the variable, the internal spacing is preserved and the expansion is treated as one argument to the printf command.

With arrays, the rules are both similar and different.

  • If groups is an array, referencing $groups or ${groups} is tantamount to referencing ${groups[0]}, the zeroth element of the array.
  • Referencing "${groups[@]}" is analogous to referencing "$@"; it preserves the spacing in the individual elements of the array, and returns a list of values, one value per element of the array.
  • Referencing ${groups[@]} without the double quotes does not preserve spacing and can introduce more values than there are elements in the array if some of the elements contain spaces.

For example:

$ groups=("abc def" "  pqr  xyz  ")
$ printf "X%sX\n" ${groups[@]}
XabcX
XdefX
XpqrX
XxyzX
$ printf "X%sX\n" "${groups[@]}"
Xabc defX
X  pqr  xyz  X
$ printf "X%sX\n" $groups
XabcX
XdefX
$ printf "X%sX\n" "$groups"
Xabc defX
$

Using * instead of @ leads to subtly different results.

See also How to iterate over the arguments in a bash script.


The second sentence of the first paragraph under Parameter Expansion in man bash says,

The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.

Which tells you that the name is simply braces, and the main purpose is to clarify where the name begins and ends:

foo='bar'
echo "$foobar"
# nothing
echo "${foo}bar"
barbar

If you read further you discover,

The braces are required when parameter is a positional parameter with more than one digit…

Let's test:

$ set -- {0..100}
$ echo $22
12
$ echo ${22}
20

Huh. Neat. I honestly didn't know that before writing this (I've never had more than 9 positional parameters before.)

Of course, you also need braces to do the powerful parameter expansion features like

${parameter:-word}
${parameter:=word}
${parameter:?word}
… [read the section for more]

as well as array expansion.


A related case not covered above. Quoting an empty variable seems to change things for test -n. This is specifically given as an example in the info text for coreutils, but not really explained:

16.3.4 String tests
-------------------

These options test string characteristics.  You may need to quote
STRING arguments for the shell.  For example:

     test -n "$V"

  The quotes here prevent the wrong arguments from being passed to
`test' if `$V' is empty or contains special characters.

I'd love to hear the detailed explanation. My testing confirms this, and I'm now quoting my variables for all string tests, to avoid having -z and -n return the same result.

$ unset a
$ if [ -z $a ]; then echo unset; else echo set; fi
unset
$ if [ -n $a ]; then echo set; else echo unset; fi    
set                                                   # highly unexpected!

$ unset a
$ if [ -z "$a" ]; then echo unset; else echo set; fi
unset
$ if [ -n "$a" ]; then echo set; else echo unset; fi
unset                                                 # much better

Well, I know that encapsulation of a variable helps you to work with something like:

${groups%example}

or syntax like that, where you want to do something with your variable before returning the value.

Now, if you see your code, all the magic is inside

${groups[@]}

the magic is in there because you can't write just: $groups[@]

You're putting your variable inside the {} because you want to use special characters [] and @. You can't name or call your variable just: @ or something[] because these are reserved characters for other operations and names.