Bash Bonanza: Quoting and Escaping Part 1

Bash can be a bit tricky and counter-intuitive at times. With the Bash Bonanza series of blog posts, I aim to provide you with bash tricks that I use on a regular basis to retain my sanity greatly improve my workflow.

I will start with one of the most important pieces of knowledge when using bash - how to properly escape strings.

You have three mechanisms of escaping available to you: escape characters (the backslash character in bash), double quotes, and single quotes.

Of all of these, the only one that does not have exceptions is the single quote!

For example: double quotes will expand certain characters, such as $. A backslash followed by a newline character tells bash that there is more input to be followed, and the backslash is removed from the input.

Most of the time, I just want to pass a literal string without worrying about exceptional cases (especially when generating bash commands from another programming language with its own escaping rules). The single quote is ideal for this purpose.

Inside single quotes, everything is literal. A backslash is backslash, an asterisk is an asterisk, a dollar sign is a dollar sign. The only thing that can rain on your parade is another single quote, as it will close the opening single quote.

Since we don't want to be thrust out into the big bad world outside the single quote confines, we're going to use another bash trick - namely, the fact that if you concatenate several quoted strings, regardless of quote type, they are counted as a single argument.

I will demonstrate what I mean in the series of commands below.

# Several concatenated double quoted strings are a single argument
$ printf '%s\n' "abc""def""ghi"
abcdefghi

# Several concatenated strings surrounded by differing quotes are still a single argument
$ printf '%s\n' "abc"'def'"ghi"
abcdefghi

# Whitespace between the quoted strings make them seperate arguments
$ printf '%s\n' 'abc' "def"'ghi'
abc  
defghi  

With this in mind, the strategy is as follows: surround your input in single quotes, and replace all instances of single quotes in the input with '"'"'.

This does the following: the first single quote will close the opening single quote. Next you are concatenating a double quoted string, containing only a single quote - "'". Finally, you are reopening the single quote and continuing. That's pretty much the only thing you need to remember!

The example below will make it clearer: let's say I'm trying to pass my username as an argument to a script, and my username is my$finger'sli\pped.

$ printf '%s\n' 'my$finger'"'"'sli\pped'
my$finger'sli\pped  

Awesome! As long as you don't need the expansion offered by double quotes, this works perfectly. This trick alone has saved me countless hours of pain, and I hope it will do the same for you.

Join me next time as we dive deeper into the high-risk high-return world of bash!