## Always use double quotes around variable substitutions and command substitutions: `"$foo"`, `"$(foo)"`
If you use `$foo` unquoted, your script will choke on input or parameters (or command output, with `$(foo)`) containing whitespace or `\[*?`.
There, you can stop reading. Well, ok, here are a few more:
* `read` — To read input line by line with the `read` builtin, use `while IFS= read -r line; do …`
Plain `read` treats backslashes and whitespace specially.
* `xargs` — Avoid `xargs`. If you must use `xargs`, make that `xargs -0`. Instead of `find … | xargs`, prefer `find … -exec …`.
`xargs` treats whitespace and the characters `\"'` specially.
This answer applies to Bourne/POSIX-style shells (`sh`, `ash`, `dash`, `bash`, `ksh`, `mksh`, …). Zsh users can skip it. If you want the whole nitty-gritty, [read the standard](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06) or your shell's manual.
----
Note that the explanations below contains a few approximations (statements that are true in most conditions but can be affected by the surrounding context or by configuration).
## Why do I need to write `"$foo"`? What happens without the quotes?
`$foo` does not mean “take the value of the variable `foo`”. It means something much more complex:
* First, take the value of the variable.
* Field splitting: treat that value as a whitespace-separated list of fields, and build the resulting list. For example, if the variable contains `foo * bar ` then the result of this step is the 3-element list `foo`, `*`, `bar`.
* Filename generation: treat each field as a glob, i.e. as a wildcard pattern, and replace it by the list of file names that match this pattern. If the pattern doesn't match any files, it is left unmodified. In our example, this results in the list containing `foo`, following by the list of files in the current directory, and finally `bar`. If the current directory is empty, the result is `foo`, `*`, `bar`.
Note that the result is a list of strings. There are two contexts in shell syntax: list context and string context. Field splitting and filename generation only happen in list context, but that's most of the time. Double quotes delimit a string context: the whole double-quoted string is a single string, not to be split. (Exception: `"$@"` — which is equivalent to e.g. `"$1" "$2" "$3"` if there are three positional parameters.)
The same happens to command substitution with `$(foo)` or with `` `foo` ``. On a side note, don't use `` `foo` ``: its quoting rules are weird and non-portable, and all modern shells support `$(foo)` which is absolutely equivalent except for having intuitive quoting rules.
See http://unix.stackexchange.com/questions/68694/when-is-double-quoting-necessary for more details.
Unless you mean for all this rigmarole to happen, just remember to always use double quotes around variable and command substitutions.
### How do I process a list of file names?
If you write `myfiles="file1 file2"`, with spaces to separate the files, this can't work with file names containing spaces. Unix file names can contain any character other than `/` (which is always a directory separator) and null bytes (which you can't use in shell scripts with most shells).
Same problem with `myfiles=*.txt; … process $myfiles`. When you do this, the variable `myfiles` contains the 5-character string `*.txt`, and it's when you write `$myfiles` that the wildcard is expanded. This example will actually work, until you change your script to be `myfiles="$someprefix*.txt"; … process $myfiles`. If `someprefix` is set to `final report`, this won't work.
To process a list of any kind (such as file names), put it in an array. This requires mksh, ksh93 or bash (or zsh, which doesn't have all these quoting issues).
myfiles=("$someprefix"*.txt)
process "${myfiles[@]}"
`ksh88` (the `ksh` of most commercial unices) uses this syntax to set the array instead:
set -A myfiles -- "$someprefix"*.txt
A plain POSIX shell (such as ash/dash or yash) has only one array: `"$@"` which you set with `set`:
set -- "$someprefix"*.txt
process "$@"
## What's up with `read`?
Without `-r`, `read` allows continuation lines — this is a single logical line of input:
hello \
world
`read` splits the input line into fields delimited by characters in `$IFS` (where without `-r`, backslash is also used to escape those). For example, if the input is a line containing three words, then `read first second third` sets `first` to the first word of input, `second` to the second word and `third` to the third word. If there are more words, the last variable contains everything that's left after setting the preceding ones. Leading and trailing whitespace are trimmed.
Setting `IFS` to the empty string avoids any trimming. See http://unix.stackexchange.com/questions/18886/why-is-while-ifs-read-used-so-often-instead-of-ifs-while-read/18936#18936 for a longer explanation.
## What's wrong with `xargs`?
The input format of `xargs` is whitespace-separated strings which can optionally be single- or double-quoted. No standard tool outputs this format.
The input to `xargs -L1` or `xargs -l` is almost a list of lines, but not quite — if there is a space at the end of a line, the following line is a continuation line.
You can use `xargs -0` where applicable (and where available: GNU (Linux, Cygwin), BusyBox, BSD, OSX, but it isn't in POSIX). That's safe, because null bytes can't appear in most data, in particular in file names. To produce a null-separated list of file names, use `find … -print0` (or you can use `find … -exec …` as explained below).
### How do I process files found by `find`?
find … -exec some_command a_parameter another_parameter {} +
`some_command` needs to be an external command, it can't be a shell function or alias. If you need to invoke a shell to process the files, call `sh` explicitly.
find … -exec sh -c '
for x do
… # process the file "$x"
done
' find-sh {} +
## I have some other question
Browse the [tag:quoting] tag on this site, or [tag:shell] or [tag:shell-script]. (Click on “learn more…” to see some general tips and a hand-selected list of common questions.) If you've searched and you can't find an answer, [ask away](http://unix.stackexchange.com/questions/ask).