Quoting and escaping is really an important way to influence the way, Bash treats your input. There are three recognized types:
\$stuff"stuff"'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:
### 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 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 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).
Inside a weak-quoted string there's no special interpretion of:
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.
If a backslash in double-quotes ("weak quoting") occurs, there are 2 ways to deal with it
In particuar this means that "\$" will become $, but "\x" will become \x.
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...'
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 |
|---|---|
\a | terminal alert character (bell) |
\b | backspace |
\e | escape (ASCII 033) |
\E | escape (ASCII 033) |
\f | form feed |
\n | newline |
\r | carriage return |
\t | horizontal tab |
\v | vertical tab |
\\ | backslash |
\' | single quote |
\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) |
\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) |
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.
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.
Attention: There is a security hole. Please read in the gettext documentation
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 doneWhy? 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
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 = testwordWhich 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".
Discussion