Let\'s say, you have a Bash alias
like:
alias rxvt=\'urxvt\'
which works fine.
However:
I can confirm that using '\''
for a single quote inside a single-quoted string does work in Bash, and it can be explained in the same way as the "gluing" argument from earlier in the thread. Suppose we have a quoted string: 'A '\''B'\'' C'
(all quotes here are single quotes). If it is passed to echo, it prints the following: A 'B' C
.
In each '\''
the first quote closes the current single-quoted string, the following \'
glues a single quote to the previous string (\'
is a way to specify a single quote without starting a quoted string), and the last quote opens another single-quoted string.
shell_escape () {
printf '%s' "'${1//\'/\'\\\'\'}'"
}
Implementation explanation:
double quotes so we can easily output wrapping single quotes and use the ${...}
syntax
bash's search and replace looks like: ${varname//search/replacement}
we're replacing '
with '\''
'\''
encodes a single '
like so:
'
ends the single quoting
\'
encodes a '
(the backslash is needed because we're not inside quotes)
'
starts up single quoting again
bash automatically concatenates strings with no white space between
there's a \
before every \
and '
because that's the escaping rules for ${...//.../...}
.
string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded: $(shell_escape "$string")"
echo "expanded: $(bash -c "echo $(shell_escape "$string")")"
P.S. Always encode to single quoted strings because they are way simpler than double quoted strings.
Simple example of escaping quotes in shell:
$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc
It's done by finishing already opened one ('
), placing escaped one (\'
), then opening another one ('
). This syntax works for all commands. It's very similar approach to the 1st answer.
Obviously, it would be easier simply to surround with double quotes, but where's the challenge in that? Here is the answer using only single quotes. I'm using a variable instead of alias
so that's it's easier to print for proof, but it's the same as using alias
.
$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'
Explanation
The key is that you can close the single quote and re-open it as many times as you want. For example foo='a''b'
is the same as foo='ab'
. So you can close the single quote, throw in a literal single quote \'
, then reopen the next single quote.
Breakdown diagram
This diagram makes it clear by using brackets to show where the single quotes are opened and closed. Quotes are not "nested" like parentheses can be. You can also pay attention to the color highlighting, which is correctly applied. The quoted strings are maroon, whereas the \'
is black.
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\' # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^ # show open/close quotes
urxvt -fg ' #111111 ' -bg ' #111111 ' # literal characters remaining
(This is essentially the same answer as Adrian's, but I feel this explains it better. Also his answer has 2 superfluous single quotes at the end.)
IMHO the real answer is that you can't escape single-quotes within single-quoted strings.
Its impossible.
If we presume we are using bash.
From bash manual...
Enclosing characters in single quotes preserves the literal value of each
character within the quotes. A single quote may not occur
between single quotes, even when preceded by a backslash.
You need to use one of the other string escape mechanisms " or \
There is nothing magic about alias
that demands it use single quotes.
Both the following work in bash.
alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'
The latter is using \ to escape the space character.
There is also nothing magic about #111111 that requires single quotes.
The following options achieves the same result the other two options, in that the rxvt alias works as expected.
alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""
You can also escape the troublesome # directly
alias rxvt="urxvt -fg \#111111 -bg \#111111"
This function:
quote ()
{
local quoted=${1//\'/\'\\\'\'};
printf "'%s'" "$quoted"
}
allows quoting of '
inside '
. Use as this:
$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
If the line to quote gets more complex, like double quotes mixed with single quotes, it may become quite tricky to get the string to quote inside a variable. When such cases show up, write the exact line that you need to quote inside an script (similar to this).
#!/bin/bash
quote ()
{
local quoted=${1//\'/\'\\\'\'};
printf "'%s'" "$quoted"
}
while read line; do
quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_
Will output:
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'
All correctly quoted strings inside single quotes.