Table of Contents

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.

Beside these common basic variants, there are some more special quoting methods (like interpreting ANSI-C escapes in a string) you'll meet below.

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:

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:

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

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:

Code

Meaning

\"

double-quote

\'

single-quote

\\

backslash

\a

terminal alert character (bell)

\b

backspace

\e

escape (ASCII 033)

\E

escape (ASCII 033) \E is non-standard

\f

form feed

\n

newline

\r

carriage return

\t

horizontal tab

\v

vertical tab

\cx

a control-x character, for example $'\cZ' to print the control sequence composed by Ctrl-Z (^Z)

\uXXXX

Interprets XXXX as hexadecimal number and prints the corresponding character from the character set (4 digits) (Bash 4.2-alpha)

\UXXXXXXXX

Interprets XXXX as hexadecimal number and prints the corresponding character from the character set (8 digits) (Bash 4.2-alpha)

\nnn

the eight-bit character whose value is the octal value nnn (one to three digits)

\xHH

the eight-bit character whose value is the hexadecimal value HH (one or two hex digits)

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.

The $'...' syntax comes from ksh93, but is portable to most modern shells including pdksh. A specification for it was accepted for SUS issue 7. There are still some stragglers, such as most ash variants including dash, (except busybox built with "bash compatibility" features).

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 _().

See also

Thanks for posting this Great article! It really helped my see these magic things of bash more clearly.
But I still have a problem that I can't solve. I'll post it here, maybe someone can help me.

So here is it:
I have a test folder with some subfolders:

# find .
.
./etc
./etc/a
./a
./a/b
./e
./c
./c/d

I want to list all folders and files except the etc folder and its contents
I'll use this command, and i get exactly what i want:

# find . ! -wholename "./etc*"
.
./a
./a/b
./e
./c
./c/d

Till this point it's fine. But inside a bash script I NEED THIS AS STRING because i'm building up the condition based on some internal values. Now look at this:

# cond='! -wholename "./etc*"'

And when i run find again…

# find . $cond
.
./etc
./etc/a
./a
./a/b
./e
./c
./c/d

…it lists the etc folder and its contents which it shouldn't.

I'm sure it's a quotation problem, and i tried all variations i know, but i wasn't able to solve the problem.
Where is the mistake?

I appreciate your help
Szilvi

Jan Schampera, 2012/09/03 11:27

Yea, this is a quoting problem.

The text you write in the variable ("./etc") is really text. The quotes you give on commandline (find . ! -wholename "./etc*") is syntax. You can't "store syntax in variables". The syntax (quoting) is used to tell Bash what a word is when it can't automatically detect it (and especially here, to not make Bash expand the wildcard itself, but to pass it as text to find).

In general, you should construct an array where every element contains one "word" and the whole array forms the arguments you want to pass to find: