Quotes and escaping

Quoting and escaping is really an important way to influence the way, Bash treats your input. There are three recognized types:

  • per-character escaping using a backslash: \$stuff
  • weak quoting with double-quotes: “stuff”
  • strong quoting with single-quotes: 'stuff'

All three forms have the very same purpose: They give you general control over parsing, expansion and expansion's results.

:!: ATTENTION :!: These quote characters (, double quote and ', single quote) are a syntax element that influences parsing. It is not related to eventual quote characters that are passed as text to the commandline! The syntax-quotes are removed before the command is called! Look:

### NO NO NO: this passes three strings:
###      (1)  "my
###      (2)  multiword
###      (3)  argument"
MYARG="\"my multiword argument\""
somecommand $MYARG

### THIS IS NOT (!!!!) THE SAME AS ###
command "my multiword argument"

### YOU NEED ###
MYARG="my multiword argument"
command "$MYARG"

Per-character escaping

Per-character escaping is useful in different places, also here, on expansions and substitutions. In general, a character that has a special meaning for Bash, like the dollar-sign ($) to introduce some expansion types, can be masked to not have that special meaning using the backslash:

echo \$HOME is set to \"$HOME\"

  • \$HOME won't expand because it's not variable expansion syntax anymore
  • The quotes are masked with the backslash to be literal - otherwise they would be interpreted by Bash

The sequence \<newline> (an unquoted backslash, followed by a <newline> character) is interpreted as line continuation. It is removed from the input stream and thus effectively ignored. Use it to beautify your code:

# escapestr_sed()
# read a stream from stdin and escape characters in text that could be interpreted as
# special characters by sed
escape_sed() {
 sed \
  -e 's/\//\\\//g' \
  -e 's/\&/\\\&/g'
}

The backslash can be used to mask every character that has a special meaning for bash. Exception: Inside a single-quoted string (see below).

Weak quoting

Inside a weak-quoted string there's no special interpretion of:

  • spaces as word-separators (on inital commandline splitting and on word splitting!)
  • single-quotes to introduce strong-quoting (see below)
  • characters for pattern matching
  • pathname expansion
  • process substitution

Everything else, especially parameter expansion, is performed!

ls -l "*"
Will not be expanded. ls gets the literal * as argument. It will, unless you have a file named *, spit out an error.

echo "Your PATH is: $PATH"
Will work as expected. $PATH is expanded, because it's only double- (weak-) quoted.

Strong quoting

Strong quoting is very easy to explain:

Inside a single-quoted string nothing(!!!!) is interpreted, except the single-quote that closes the quoting.

echo 'Your PATH is: $PATH'
That $PATH won't be expanded, it's interpreted as normal ordinary text, because it's surrounded by strong quotes.

In practise that means, to produce a text like Here's my test... as a single-quoted string, you have to leave and re-enter the single-quoting to get the character ”'” as literal text:

# WRONG
echo 'Here's my test...'

# RIGHT
echo 'Here'\''s my test...'

ANSI C like strings

There's another quoting mechanism, Bash provides: Strings that are scanned for ANSI C like escape sequences. The Syntax is

$'string'
where the following escape sequences are decoded in string:

CodeMeaning
\aterminal alert character (bell)
\bbackspace
\eescape (ASCII 033)
\fform feed
\nnewline
\rcarriage return
\thorizontal tab
\vvertical tab
\\backslash
\'single quote
\nnnthe eight-bit character whose value is the octal value nnn (one to three digits)
\xHHthe eight-bit character whose value is the hexadecimal value HH (one or two hex digits)
\cxa control-x character, for example $'\cZ to print the control sequence composed by Ctrl-Z (^Z)

This is especially useful when you want to give special characters as arguments to some programs, like giving a newline to sed.

The resulting text is treated as if it was single-quoted. No further expansions happen.

I18N/L10N

A dollar-sign followed by a double-quoted string, for example

echo $"generating database..."
means I18N. If there is a translation available for that string, it is used instead of the given text. If not, or if the locale is C/POSIX, the dollar sign simply is ignored, which results in a normal double-quoted string.

If the string was replaced (translated), the result is double-quoted.

In case you're a C-programmer: The purpose of $”...” is the same as for gettext() or _().

For useful examples to localize your scripts, please see Appendix I of the Advanced Bash Scripting Guide.

Common mistakes

String lists in for-loops

The classic for-loop uses a list of words to iterate through. This list can - of course - also be in a variable:

mylist="DOG CAT BIRD HORSE"

WRONG way to iterate through this list:

for animal in "$mylist"; do
  echo $animal
done
Why? Due to the double-quotes, technically, the expansion of $mylist is seen as one word. The for-loop iterates exactly one time, with animal set to the whole list.

RIGHT way to iterate through this list:

for animal in $mylist; do
  echo $animal
done

Working out the test-command

The command test or [ ... ] ( the classic test command) is a normal ordinary command, so normal ordinary syntax rules apply. Let's take string comparison as example:

[ WORD = WORD ]

The ] at the end is a convenience; if you type which [ you will see that there is in fact a binary with that name. So if we were writing this as just a test command it would be:

test WORD = WORD

When you compare variables, it's wise to quote them. Let's invent a test string with spaces:

mystring="my string"

And now check that string against the word “testword”:

[ $mystring = testword ] # WRONG!!!
This fails! These are too much arguments for the string comparison test. After all expansions performed you really execute:
[ my string = testword ]
test my string = testword
Which is wrong, because my and string are two separate arguments.

So what you really want to do is:

[ "$mystring" = testword ] # RIGHT!!!

test 'my string' = testword

Now the command has three parameters, which makes sense for a binary (two argument) operator.

Hint: Inside the conditional expression ([[ ]]) Bash doesn't perform word splitting, and thus you don't need to quote your variable references - they are always seen as “one word”.

See also

syntax/quoting.txt · Last modified: 2009/02/24 07:18 (external edit)
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0