To quote, or not to quote, that is the question

You can feel alone when learning shell

When working with my workmates on some command line — so every time I work in pair in fact — I often hear the same question: with quotes? simple or double?

I would like to explain once for all when to put quotes around your shell (here I will speak only about bash) arguments, and when not, and why.

Do I need to quote my password?

Quotes are here to not have to escape some characters that would be interpreted differently by bash. They have almost no relation why the tool you are calling, even if it’s a node runtime, a scala runtime, another shell script or an ELF executable (binary).

The basic case is if you have a space in a file name, for example crazy file name with spaces. If you don’t escape spaces, you turn to pass 5 arguments to your command: crazy,file,name,with and spaces. So if you want to remove this file you end up removing 5 unrelated files. And not the one you wanted to remove. To remove this file, you need to escape the spaces:

$ rm crazy\ file\ name\ with\ spaces
$ rm "crazy file name with spaces"
$ rm 'crazy file name with spaces'

That’s the basics. But to be sure you understand the principle, the following sample is also valid:

$ rm crazy\ file" "nam'e 'with" spac"es

But I’m sure you prefer the previous ones!

The interesting part is as you can see, quotes are only there to escape and don’t split argument by themselves. It’s only the internal field separator (IFS, it can be changed, but that’s another story) that is set to space by default.

Can I haz my vars?

Now that you understand when you need to quote, you’ll be happy to differentiate between simple and double quotes.

As already said, quoting is done to prevent the shell from interpreting some special (for the shell, not for you) characters: spaces as we seen, but also > and < (for redirections), parenthesis (for sub processing), ! (history expansion, very useful to save you some typing), ` (back-quote, for getting a result from a sub command), $ (to resolve variables for example), and of course… escaping characters themselves: ", ' and \ which you would like to sometimes escape too.

With simple quotes this is very simple: you escape everything, i.e. everything you write between simple quotes will be taken as literal. But simple quotes themselves. Because they will end the escaping, and so you cannot escape them. If you need them either you use double quotes, which are effectively escaping simple quotes, or you use some tricks:

$ cat "I'm learning to quote with strange filenames.txt"
$ cat 'I'\''m learning to quote with strange filenames.txt'
$ cat 'I'"'"'m learning to quote with strange filenames.txt'

But double quotes are not only for escaping simple quotes. They let also the shell interpret a useful subset of special chars: $, \, ` and !. For example:

$ var=myvalue
$ echo "with double quotes \$var is interpreted as $var"
with double quotes $var is interpreted as myvalue
$ echo 'with single quotes $var is not interpreted'
with single quotes $var is not interpreted
$ echo hello
$ echo "last param was $!"
last param was hello

Minus + minus = plus?

If you have well followed, you have remarked that we have never spoke about the minus character, which is used as a de facto standard in the CLI (Command Line Interface) for options. How do you remove a file named -f for example? Or even worst a file named -f /?

There is a convention that is widespread between most CLI, it’s to use -- for that. All parameters written after -- will be taken as files, not as options. So problem solved:

$ rm -- -f
$ rm -- "-f /"

Going further?

Now that you know how to use quoting, you can be proud to use filenames with spaces in, real passwords (who writes passwords in command lines tools anyway?), and have a hankering after writing shell scripts. So last but no least, how do you take all parameters of a shell script and pass them to another command?

wrappedcommand "$@"

That’s a special case of using double quotes. I hope that made you want to write shell scripts… or not!