Let\'s say, you have a Bash alias
like:
alias rxvt=\'urxvt\'
which works fine.
However:
Here are my two cents -- in the case if one wants to be sh
-portable, not just bash
-specific ( the solution is not too efficient, though, as it starts an external program -- sed
):
quote.sh
( or just quote
) somewhere on your PATH
:# this works with standard input (stdin) quote() { echo -n "'" ; sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' ; echo -n "'" } case "$1" in -) quote ;; *) echo "usage: cat ... | quote - # single-quotes input for Bourne shell" 2>&1 ;; esac
An example:
$ echo -n "G'day, mate!" | ./quote.sh - 'G'"'"'day, mate!'
And, of course, that converts back:
$ echo 'G'"'"'day, mate!' G'day, mate!
Explanation: basically we have to enclose the input with quotes '
, and then also replace any single quote within with this micro-monster: '"'"'
( end the opening quote with a pairing '
, escape the found single quote by wrapping it with double quotes -- "'"
, and then finally issue a new opening single quote '
, or in pseudo-notation : ' + "'" + ' == '"'"'
)
One standard way to do that would be to use sed
with the following substitution command:
s/\(['][']*\)/'"\1"'/g
One small problem, though, is that in order to use that in shell one needs to escape all these single quote characters in the sed expression itself -- what leads to something like
sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g'
( and one good way to build this result is to feed the original expression s/\(['][']*\)/'"\1"'/g
to Kyle Rose' or George V. Reilly's scripts ).
Finally, it kind of makes sense to expect the input to come from stdin
-- since passing it through command-line arguments could be already too much trouble.
( Oh, and may be we want to add a small help message so that the script does not hang when someone just runs it as ./quote.sh --help
wondering what it does. )
Here is an elaboration on The One True Answer referenced above:
Sometimes I will be downloading using rsync over ssh and have to escape a filename with a ' in it TWICE! (OMG!) Once for bash and once for ssh. The same principle of alternating quotation delimiters is at work here.
For example, let's say we want to get: Louis Theroux's LA Stories ...
And behold! You wind up with this:
rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'
which is an awful lot of work for one little ' -- but there you go
I don't see the entry on his blog (link pls?) but according to the gnu reference 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.
so bash won't understand:
alias x='y \'z '
however, you can do this if you surround with double quotes:
alias x="echo \'y "
> x
> 'y
Here is another solution. This function will take a single argument and appropriately quote it using the single-quote character, just as the voted answer above explains:
single_quote() {
local quoted="'"
local i=0
while [ $i -lt ${#1} ]; do
local ch="${1:i:1}"
if [[ "$ch" != "'" ]]; then
quoted="$quoted$ch"
else
local single_quotes="'"
local j=1
while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
single_quotes="$single_quotes'"
((j++))
done
quoted="$quoted'\"$single_quotes\"'"
((i+=j-1))
fi
((i++))
done
echo "$quoted'"
}
So, you can use it this way:
single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''
x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
If you're generating the shell string within Python 2 or Python 3, the following may help to quote the arguments:
#!/usr/bin/env python
from __future__ import print_function
try: # py3
from shlex import quote as shlex_quote
except ImportError: # py2
from pipes import quote as shlex_quote
s = """foo ain't "bad" so there!"""
print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))
This will output:
foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
I just use shell codes.. e.g. \x27
or \\x22
as applicable. No hassle, ever really.