10 ene 2016

Escaping single quotes in bash

The problem

We want to define an alias for deleting python .pyc files. The alias definition:
alias rmpyc="find . -name '*.pyc' -delete
does not work correctly

Solution

Combining the bash rules for quoting:
  • Any variable and pattern is escaped whenever it is NOT enclosed within single quotes
  • Backslash escaped single quotes NOT enclosed within single quotes produce literal single quotes
  • Expressions with different quotes can be directly combined, e.g.:
echo "Double quotes: "'"'' and single quotes: '"'" 
Applying both rules we can write:
1. alias rmpyc='find . -name '"'"'*-pyc'"'"' -delete'
or
2. alias rmpyc="find . -name '"'*.pyc'"' -delete"
or
3. alias rmpyc='find . -name '\''*-pyc'\'' -delete'

Detailed explanations

  1. We start using single quotes because we want to define a literal. To obtain a literal single quote, we stop quoting the first literal (find . -name) and concatenate a single quote enclosed within double quotes, then we write the file pattern within single quotes to avoid its expansion. Again, we concatenate a single quote enclosed within double quotes and finally we add the rest of the string ( -delete).
  2. We start with double quotes while there is no pattern to be expanded. This allows us to include literal single quote. After we ended the first string (after the first literal single quote), we concatenate the file pattern enclosed within single quotes to avoid file pattern expansion. Finally we add the rest of the string within double quotes (starting with the second literal single quote).
  3. We start with single quotes like in 1. until we want a literal single quote. To get it, we end the first string (with a single quote) and then we write an escaped single quote (backslash single quote). We continue with the file pattern enclosed within single quotes to avoid its expansion. Then we add the second escaped single quote like before. Finally we concatenate the rest of the string.