Differences between revisions 6 and 32 (spanning 26 versions)
Revision 6 as of 2011-04-21 19:44:39
Size: 5404
Editor: GreyCat
Comment: "..."'...'
Revision 32 as of 2024-03-07 22:57:49
Size: 11187
Editor: emanuele6
Comment: punctuation
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
Quoting in shell programming is extremely important. There are multiple types of quotes, and you must know how and when to use each type.

= Standard quoting =

A shell command is parsed by the shell into words, using whitespace (regardless of the [[IFS]] variable) and other shell metacharacters. The first function of quoting is to permit words to contain these metacharacters.
In many languages, quotes are primarily used to denote string literals. In the shell paradigm, many constructs are interpreted as strings by default, so quotes play other important roles. Quotes demarcate the arguments of a command into units called "words", as well as modify the evaluation of their contents in numerous context-dependent ways. It is critical to understand how quoting affects the interpretation of code in a given context; it's something no one should avoid learning. Improper quoting is among the most common shell programming errors. Do not guess about quotes!

<<TableOfContents>>

== I'm Too Lazy to Read, Just Tell Me What to Do ==
{{{
cp $file $destination # WRONG
cp -- "$file" "$destination" # Right
}}}

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

== Types of quoting ==
There are three standard types of quotes (or four if you count backslash escaping), and two nonstandard Bash extensions.

 * '''Single quotes''': {{{'...'}}} removes the special meaning of every character between the quotes. Everything inside single quotes becomes a ''literal string''. The only character that you can't safely enclose in single quotes is a single quote.

 * '''Double quotes''': {{{"..."}}} prevents some substitutions but allows others. Every substitution that begins with a dollar sign ''$'' is performed, as is the legacy {{{`...`}}} (backtick) [[CommandSubstitution|command substitution]]. Backslash escaping is also performed. No word splitting or filename expansion is performed.

 * '''Backticks''': {{{`...`}}} is the legacy command substitution syntax; deprecated in favor of {{{$(...)}}} but still permitted for historical reasons. See [[BashFAQ/082|FAQ 082]] for details.

 * '''Backslash''': Putting `\` in front of a metacharacter removes its special meaning. This works inside double quotes, or in the absence of quotes. It does not work inside single quotes.

 * '''$'...' ''': Contents are a single word with interpretation of backslash escape sequences such as ''\n'' for newline, ''\t'' for tab, and ''\xnn'' for bytes specified in hexadecimal. These may be used to specify a text representation of arbitrary data. No current implementation supports a context where these are not interpreted as NUL-terminated C strings.

 * '''$"..."''' : This is a Bash extension. It is used for [[BashFAQ/098|localization support]] and will not be covered on this page.

You may concatenate the various types of quoting if you need to. 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
$ printf '%s\n' '!%$*&'"$foo"
!%$*&bar
}}}
Any number of quoted substrings, of any style, may be concatenated in this manner. The result (after appropriate expansions in the double-quoted sections) is a single word.


== Effects of Quoting ==
=== Preserve unescaped metacharacters ===
A shell command is parsed by the shell into words, using whitespace other metacharacters. '''The first function of quoting is to permit words to contain these metacharacters.'''
Line 10: Line 45:
Line 13: Line 47:
The quotes are not actually passed along to the command. They are removed by the shell. In the example above, the `echo` command sees ''only'' the `&`, not the quotes.

'''Single quotes''' (apostrophes) prevent all interpretation of the characters between them.

'''Double quotes''' permit parameter expansions, [[ArithmeticExpression|arithmetic expansions]] and [[CommandSubstitution|command substitutions]] to occur inside them. They do not allow [[glob|filename expansions]], brace expansions, [[ProcessSubstitution|process substitutions]], tilde expansion, etc. In short, any substitution that starts with `$` is allowed, and also {{{`}}} for legacy compatibility.

The second purpose of quoting is to prevent WordSplitting and [[glob|globbing]]. The value of a double-quoted substitution does ''not'' undergo any further processing. (An unquoted substitution ''does''.)
The quotes are not actually passed along to the command. They are removed by the shell (this process is cleverly called "quote removal"). In the example above, the `echo` command sees ''only'' the `&`, not the quotes.

=== Prevent field splitting and ignore glob pattern characters ===
'''The second purpose of quoting is to prevent [[WordSplitting|word splitting]] and [[glob|globbing]].''' The result of a double-quoted substitution does ''not'' undergo any further processing (whereas the result of an unquoted substitution ''does'').
Line 24: Line 55:

In this example, the double quotes protect each parameter (variable) 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.
In this example, the double quotes protect the value of each parameter (variable) 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.
Line 29: Line 59:
When in doubt, quote your parameter expansions. It's rare that you would need one to be unquoted, and if that's the case, you'll know it.

You may concatenate the various types of quoting if you need to. 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:

{{{
echo '!%$*&'"$foo"
}}}

Any number of quoted substrings, of any style, may be concatenated in this manner. The result (after appropriate expansions in the double-quoted sections) is a single word.

Assorted examples, to show how things ''should'' be done. Some of these examples use bash/ksh syntax that won't work in strict POSIX shells.

{{{
for file in "$@"
}}}

{{{
for element in "${array[@]}"
}}}

{{{
for index in ${!array[*]}
# We omit quotes here because the indices are a list of numbers,
# which must be word-split in order to be processed. Numbers will
# never undergo glob expansion either, so we can skip "set -f".

# We could also write it this way, and it would still work:
for index in "${!array[@]}"

# The second version has the advantage of continuing to work if
# the array is an associative array rather than a normal array.
}}}

{{{
find_opts=('(' -iname '*.jpg' -o -iname '*.gif' -o -iname '*.png' ')')
When in doubt, always double-quote your parameter expansions.

=== 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.

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.

== When Should You Quote? ==
The basic rule of thumb is that you should double-quote every expansion. This prevents unwanted word splitting and globbing. When in doubt, quote it.

There are a few cases where double quotes may be safely omitted:
 * On the right hand side of a ''simple'' assignment. You may write `foo=$bar` without quotes. This is POSIX compliant.

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

 * 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 `[[ -z $string ]]` safely if you wish. However, be warned that `[[ foo = $bar ]]` and `[[ foo = "$bar" ]]` act differently (see [[#Patterns|patterns]] below). The `[[` keyword is a Bash extension.

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

== Examples ==
Here are some assorted examples, to show how things ''should'' be done. Some of these examples use bash/ksh syntax that won't work in strict POSIX shells.

Proper iteration over the positional parameters using a quoted `"$@"`. Never use an unquoted `$@` or `$*`.
{{{
for file in "$@"; do
...
done
}}}

As above, except with an array:
{{{
for element in "${array[@]}"; do
...
}}}

Proper iteration over array indexes:
{{{
# bash 3.0 and higher
for index in "${!array[@]}"; do
...
}}}

All of the usual expansions apply to text within the parentheses of a compound array assignment including ''word splitting'' and ''pathname expansion'', and must be quoted and escaped in the same way as though they were to be passed as arguments to a command:
{{{
# bash or ksh93
find_opts=( \( -iname '*.jpg' -o -iname '*.gif' -o -iname '*.png' \) )
Line 67: Line 108:
{{{
echo 'Don'\''t walk!'
}}}

{{{
echo "The matching line is: $(grep foo "$filename")"
There are generally three alternatives for encoding a literal string containing quotes. Which works best depends on the context. First, a single quoted string that can contain anything other than single-quotes. In this case, an escaped, unquoted single-quote is concatenated with the argument between two single quoted strings. Second, a double-quoted string with all expansions and double-quotes within escaped. Third, a less portable equivalent using `$'...'`:
{{{
printf '%s\n' 'Don'\''t walk!'
printf '%s\n' "Don't walk!"
printf '%s\n' $'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:
{{{
printf '%s\n' "The matching line is: $(grep foo "$filename")"
Line 75: Line 121:
}}}

The third type of quote is the '''backtick''' ({{{`}}}) or '''back quote'''. It's a deprecated markup for command substitutions, used in Bourne shells before the introduction of the `$(...)` syntax. For a discussion of the difference between {{{`...`}}} and `$(...)` please see [[BashFAQ/082]].

= Bash extensions =

Bash introduces two additional forms of quoting. The first is `$'...'` which acts like single quotes except that backslash-escaped combinations are expanded as specified by the ANSI C standard. This allows a convenient way to embed nonprintable characters into strings, or to pass them as arguments.

printf '%s\n' "The matching line is: $(grep foo "$filename")"
# ^---------^ inner layer (quotes)
# ^^--------------------^ middle layer (command sub)
# ^---------------------------------------------^ outer layer (quotes)
}}}

An example showing an array being "joined" with `"${a[*]}"`:
{{{
# bash
ip=192.168.1.30
netmask=255.255.254.0
IFS=. read -ra ip_octets <<<"$ip"
IFS=. read -ra netmask_octets <<<"$netmask"
for i in 0 1 2 3; do
  ((ip_octets[i] &= netmask_octets[i]))
done
IFS=.; network="${ip_octets[*]}"; unset IFS
}}}

The ansi-c `$'...'` quoting style is used to interpret backslash escapes:
Line 89: Line 148:
The second form is `$"..."` and is used for [[BashFAQ/098|localization support]].

In addition to these, bash uses quotes to suppress the "specialness" of the right-hand-side of an `=` or `=~` operator inside a `[[` keyword. That sounds complicated, but it's simpler when shown as an example:
Examples showing backslash sequences:
{{{
# These are equivalent:

printf '%s\n' hi $'there\n'

# ksh / zsh:
print 'hi\nthere\n'
}}}

=== Patterns ===
The shell uses quotes to suppress interpretation of special syntax within [[glob|patterns]] and [[RegularExpression|regular expressions]], so that any literal or expanded string may be easily included in a pattern matching context without individually escaping each character.
Line 95: Line 163:
# unquoted foo* acts as a glob # unquoted foo* acts as a pattern
Line 101: Line 169:
# the contents of $some_re are treated as an ERE # the contents of $some_re are treated as a POSIX extended regular expression.
Line 108: Line 176:
See [[glob]] and RegularExpression for explanations of those terms. {{{
# the "quoted" branch is taken.

g=a*[b]
case $g in
    $g) echo 'unquoted pattern' ;;
    "$g") echo 'quoted pattern'
esac
}}}

=== Here Documents ===
Quote-removal never applies to the contents of a [[HereDocument|here document]]

{{{
 $ arr=(words in array); cat <<EOF
> These are the "${arr[*]}"
> EOF
These are the "words in array"
}}}

Quoting or escaping the "delimiter" in the heredoc redirection affects whether its contents are subject to expansions. The quoted variety is bash's only context in which an arbitrary string may be used with no special interpretation except for lines beginning with the delimiter, which marks the end of the heredoc.

{{{
{ printf '%s\n' before "$(</dev/stdin)" after; } <<\EOF
# Anything but "EOF" can go here
EOF
}}}

== See also ==
 * [[Arguments]]
 * http://www.grymoire.com/Unix/Quote.html
 * http://wiki.bash-hackers.org/syntax/quoting
 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02

In many languages, quotes are primarily used to denote string literals. In the shell paradigm, many constructs are interpreted as strings by default, so quotes play other important roles. Quotes demarcate the arguments of a command into units called "words", as well as modify the evaluation of their contents in numerous context-dependent ways. It is critical to understand how quoting affects the interpretation of code in a given context; it's something no one should avoid learning. Improper quoting is among the most common shell programming errors. Do not guess about quotes!

I'm Too Lazy to Read, Just Tell Me What to Do

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

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

Types of quoting

There are three standard types of quotes (or four if you count backslash escaping), and two nonstandard Bash extensions.

  • Single quotes: '...' removes the special meaning of every character between the quotes. Everything inside single quotes becomes a literal string. The only character that you can't safely enclose in single quotes is a single quote.

  • Double quotes: "..." prevents some substitutions but allows others. Every substitution that begins with a dollar sign $ is performed, as is the legacy `...` (backtick) command substitution. Backslash escaping is also performed. No word splitting or filename expansion is performed.

  • Backticks: `...` is the legacy command substitution syntax; deprecated in favor of $(...) but still permitted for historical reasons. See FAQ 082 for details.

  • Backslash: Putting \ in front of a metacharacter removes its special meaning. This works inside double quotes, or in the absence of quotes. It does not work inside single quotes.

  • $'...' : Contents are a single word with interpretation of backslash escape sequences such as \n for newline, \t for tab, and \xnn for bytes specified in hexadecimal. These may be used to specify a text representation of arbitrary data. No current implementation supports a context where these are not interpreted as NUL-terminated C strings.

  • $"..." : This is a Bash extension. It is used for localization support and will not be covered on this page.

You may concatenate the various types of quoting if you need to. 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
$ printf '%s\n' '!%$*&'"$foo"
!%$*&bar

Any number of quoted substrings, of any style, may be concatenated in this manner. The result (after appropriate expansions in the double-quoted sections) is a single word.

Effects of Quoting

Preserve unescaped metacharacters

A shell command is parsed by the shell into words, using whitespace other metacharacters. The first function of quoting is to permit words to contain these metacharacters.

echo '&'

Without quotes, the & would put the echo command into the background. With quotes, the & is simply made into a word, and passed as an argument to the echo command instead.

The quotes are not actually passed along to the command. They are removed by the shell (this process is cleverly called "quote removal"). In the example above, the echo command sees only the &, not the quotes.

Prevent field splitting and ignore glob pattern characters

The second purpose of quoting is to prevent word splitting and globbing. The result of a double-quoted substitution does not undergo any further processing (whereas the result of an unquoted substitution does).

cp -- "$filename" "$destination"

In this example, the double quotes protect the value of each parameter (variable) 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.

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.

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.

When Should You Quote?

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

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

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

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

  • 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 [[ -z $string ]] safely if you wish. However, be warned that [[ foo = $bar ]] and [[ foo = "$bar" ]] act differently (see patterns below). The [[ keyword is a Bash extension.

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

Examples

Here are some assorted examples, to show how things should be done. Some of these examples use bash/ksh syntax that won't work in strict POSIX shells.

Proper iteration over the positional parameters using a quoted "$@". Never use an unquoted $@ or $*.

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

As above, except with an array:

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

Proper iteration over array indexes:

# bash 3.0 and higher
for index in "${!array[@]}"; do
...

All of the usual expansions apply to text within the parentheses of a compound array assignment including word splitting and pathname expansion, and must be quoted and escaped in the same way as though they were to be passed as arguments to a command:

# bash or ksh93
find_opts=( \( -iname '*.jpg' -o -iname '*.gif' -o -iname '*.png' \) )
find . "${find_opts[@]}" -print

There are generally three alternatives for encoding a literal string containing quotes. Which works best depends on the context. First, a single quoted string that can contain anything other than single-quotes. In this case, an escaped, unquoted single-quote is concatenated with the argument between two single quoted strings. Second, a double-quoted string with all expansions and double-quotes within escaped. Third, a less portable equivalent using $'...':

printf '%s\n' 'Don'\''t walk!'
printf '%s\n' "Don't walk!"
printf '%s\n' $'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:

printf '%s\n' "The matching line is: $(grep foo "$filename")"

# Note that the quotes inside the $() command substitution are nested.
# This looks wrong to a C programmer, but it is correct in shells.

printf '%s\n' "The matching line is: $(grep foo "$filename")"
#                                               ^---------^    inner layer (quotes)
#                                    ^^--------------------^   middle layer (command sub)
#             ^---------------------------------------------^  outer layer (quotes)

An example showing an array being "joined" with "${a[*]}":

# bash
ip=192.168.1.30
netmask=255.255.254.0
IFS=. read -ra ip_octets <<<"$ip"
IFS=. read -ra netmask_octets <<<"$netmask"
for i in 0 1 2 3; do
  ((ip_octets[i] &= netmask_octets[i]))
done
IFS=.; network="${ip_octets[*]}"; unset IFS

The ansi-c $'...' quoting style is used to interpret backslash escapes:

IFS=$' \t\n'
# sets the IFS variable to the three-byte string containing
# a space, a tab, and a newline

Examples showing backslash sequences:

# These are equivalent:

printf '%s\n' hi $'there\n'

# ksh / zsh:
print 'hi\nthere\n'

Patterns

The shell uses quotes to suppress interpretation of special syntax within patterns and regular expressions, so that any literal or expanded string may be easily included in a pattern matching context without individually escaping each character.

if [[ $path = foo* ]]; then
# unquoted foo* acts as a pattern

if [[ $path = "foo*" ]]; then
# quoted "foo*" is a literal string

if [[ $path =~ $some_re ]]; then
# the contents of $some_re are treated as a POSIX extended regular expression.

if [[ $path =~ "$some_re" ]]; then
# the contents of $some_re are treated as a literal string
# despite the =~ operator

# the "quoted" branch is taken.

g=a*[b]
case $g in
    $g) echo 'unquoted pattern' ;;
    "$g") echo 'quoted pattern'
esac

Here Documents

Quote-removal never applies to the contents of a here document

 $ arr=(words in array); cat <<EOF
> These are the "${arr[*]}"
> EOF
These are the "words in array"

Quoting or escaping the "delimiter" in the heredoc redirection affects whether its contents are subject to expansions. The quoted variety is bash's only context in which an arbitrary string may be used with no special interpretation except for lines beginning with the delimiter, which marks the end of the heredoc.

{ printf '%s\n' before "$(</dev/stdin)" after; } <<\EOF
# Anything but "EOF" can go here
EOF

See also


CategoryShell

Quotes (last edited 2024-03-07 22:57:49 by emanuele6)