Table of Contents

BASH - Quotes

Many different types of quotes enable different ways of interpreting their contents.

Unfortunately, the rules and their behavior varies by context with several special cases and exceptions to remember.

TIP: When in doubt, double-quote every expansion in your shell commands.

The basic rule of thumb is that you should double-quote every expansion. This prevents unwanted word splitting and globbing.

There are a few cases where double quotes may be safely omitted:

  • On the right hand side of a simple assignment. You may write the following without quotes. This is POSIX compliant.
    foo=$bar

  • The word following a case keyword. You may write the following safely. This is POSIX compliant.
    case $foo in ...

  • Inside a [[ command, except on the right hand side of an = or == operator. [[ already suppresses word splitting and globbing, so you can write things like the following safely if you wish.
    [[ -z $string ]]

  • WARNING: However, be warned that quoted and unquoted can act differently. The [[ keyword is a Bash extension.

    [[ foo = $bar ]] and [[ foo = "$bar" ]]

    These do act differently.

Use single quotes when protecting complex strings, especially ones that contain shell syntax which you don't want evaluated.


Types of Quotes

There are 3 types of quotes. Each of the quotes bring different meaning and usage.

' (single quotes) - Everything wrapped in this quote won't be changed (Strong quotes)

" (double quotes) - Quotes that doesn't expand meta-characters like "*" or "?," but does expand variables and does command substitution (Weaker quotes)

` (back quotes) - To execute command.  The legacy command substitution syntax; deprecated in favor of **$(...)** but still permitted for historical reasons.

Escaping Quotes

Putting a backslash character \ in front of a quote removes its special meaning.

This works inside double quotes, or in the absence of quotes.

It does not work inside single quotes.

Example

echo 'Don'\''t walk!'
echo "Don't walk!"
echo $'Don\'t talk!'

$(…)-style command substitutions are unique in that the quoting of their contents is completely independent to their surroundings. This means you don't have to worry about nested quote escaping problems.


Concatenating Quotes

The various types of quotes can be combined, or concatenated, if needed.

For example, if you have one section of a string that has lots of special characters that you'd like to single-quote, and another section with a parameter expansion in it which must be double-quoted, you may mix them:

foo=bar
echo '!%$*&'"$foo"

returns:

!%$*&bar

The result (after appropriate expansions in the double-quoted sections) is a single word.

NOTE: Any number of quoted substrings, of any style, may be concatenated in this manner.


Expand argument lists

Double-quoting $@ or ${array[@]} has a special meaning.

“$@” expands to a list of words, with each positional parameter's value being one word.

Likewise, “${array[@]}” expands to a list of words, one per array element.

When dealing with the positional parameters or with the contents of an array as a list of words, always use the double-quoted syntax.

Example of proper iteration over the positional parameters using a quoted “$@”. Never use an unquoted $@ or $*.

for file in "$@"; do
...
done

Double-quoting $* or ${array[*]} results in one word which is the concatenation of all the positional parameters (or array elements) with the first character of IFS between them.

This is similar to the join function in some other languages, although the fact that you can only have a single join character can sometimes be a crippling limitation.

for index in "${array[@]}"; do
...

Prevent field splitting and ignore glob pattern characters

cp $file $destination         # WRONG
cp -- "$file" "$destination"  # Right

In this example, the double quotes protect the value of each parameter from undergoing word splitting or globbing should it happen to contain whitespace or wildcard characters (* or ? or […]).

Without the quotes, a filename like hot stuff.mp3 would be split into two words, and each word would be passed to the cp command as a separate argument. or, a filename that contains * with whitespace around it would produce one word for every file in the current directory. That is not what we want.

With the quotes, every character in the value of the filename parameter is treated literally, and the whole value becomes the second argument to the cp command.

When in doubt, always double-quote your parameter expansions.


Example - using back quotes within single quotes

Example of using back quotes within single quotes. Nothing is changed.

echo 'Today is `date`'
 
Today is `date`

Example of using back quotes within double quotes

Example of using back quotes within double quotes. The `date` command will be executed:

echo "Today is `date`"
 
Today is Mon May 26 09:42:50 MYT 2008