How to escape single quotes within single quoted strings?

Multi tool use
How to escape single quotes within single quoted strings?
Let's say, you have a bash alias
like:
alias
alias rxvt='urxvt'
which works fine.
However:
alias rxvt='urxvt -fg '#111111' -bg '#111111''
won't work, and neither will:
alias rxvt='urxvt -fg '#111111' -bg '#111111''
So how do you end up matching up opening and closing quotes inside a string once you have escaped quotes?
alias rxvt='urxvt -fg'''#111111''' -bg '''#111111'''
seems ungainly although it would represent the same string if you're allowed to concatenate them like that.
See also: Difference between single and double quotes in Bash.
– codeforester
Jun 2 '17 at 6:02
Nested double quotes are escapable,
"""
, so those should be used in preference to @liori's answer whenever possible.– alan
Oct 20 '17 at 18:06
"""
19 Answers
19
If you really want to use single quotes in the outermost layer, remember that you can glue both kinds of quotation. Example:
alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
# ^^^^^ ^^^^^ ^^^^^ ^^^^
# 12345 12345 12345 1234
Explanation of how '"'"'
is interpreted as just '
:
'"'"'
'
'
"
'
"
'
If you do not place any whitespaces between (1) and (2), or between (4) and (5), the shell will interpret that string as a one long word.
alias splitpath='echo $PATH | awk -F : '"'"'{print "PATH is set to"} {for (i=1;i<=NF;i++) {print "["i"]",$i}}'"'"
It works when there are both single quotes and double quotes in the alias string!– Uphill_ What '1
Jun 1 '11 at 10:09
alias splitpath='echo $PATH | awk -F : '"'"'{print "PATH is set to"} {for (i=1;i<=NF;i++) {print "["i"]",$i}}'"'"
My interpretation: bash implicitly concatenates differently quoted string expressions.
– Ben Atkin
Aug 13 '13 at 20:32
worked for me, example of double escaped single quotes:
alias serve_this_dir='ruby -rrack -e "include Rack;Handler::Thin.run Builder.new{run Directory.new'"'"''"'"'}"'
– JAMESSTONEco
Sep 12 '13 at 22:50
alias serve_this_dir='ruby -rrack -e "include Rack;Handler::Thin.run Builder.new{run Directory.new'"'"''"'"'}"'
liori: IMHO stackoverflow.com/a/16605140/149221 is more elegant :-).
– mj41
Mar 1 '14 at 13:19
I contend that
'''
is vastly more readable in most contexts than '"'"'
. In fact, the former is almost always clearly distinct within a single-quoted string, and thus is just a matter of mapping it semantically to the "it's an escaped quote" meaning, as one does with "
in double-quoted strings. Whereas the latter blends into one line of quote ticks and needs careful inspection in many cases to properly distinguish.– mtraceur
Sep 5 '16 at 23:56
'''
'"'"'
"
I always just replace each embedded single quote with the sequence: '''
(that is: quote backslash quote quote) which closes the string, appends an escaped single quote and reopens the string.
'''
I often whip up a "quotify" function in my Perl scripts to do this for me. The steps would be:
s/'/'\''/g # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.
This pretty much takes care of all cases.
Life gets more fun when you introduce eval
into your shell-scripts. You essentially have to re-quotify everything again!
eval
For example, create a Perl script called quotify containing the above statements:
#!/usr/bin/perl -pl
s/'/'\''/g;
$_ = qq['$_'];
then use it to generate a correctly-quoted string:
$ quotify
urxvt -fg '#111111' -bg '#111111'
result:
'urxvt -fg '''#111111''' -bg '''#111111''''
which can then be copy/pasted into the alias command:
alias rxvt='urxvt -fg '''#111111''' -bg '''#111111''''
(If you need to insert the command into an eval, run the quotify again:
$ quotify
alias rxvt='urxvt -fg '''#111111''' -bg '''#111111''''
result:
'alias rxvt='''urxvt -fg '''''''''#111111''''''''' -bg '''''''''#111111'''''''''''''
which can be copy/pasted into an eval:
eval 'alias rxvt='''urxvt -fg '''''''''#111111''''''''' -bg '''''''''#111111'''''''''''''
But this isn't perl. And as Steve B pointed out above, with his reference to the "gnu reference manual", you can't escape quotes in bash within the same type of quote. And in fact, don't need to escape them within alternate quotes, e.g. "'" is a valid single-quote string and '"' is a valid double-quote string without requiring any escaping.
– nicerobot
Aug 22 '09 at 5:36
@nicerobot: I've added an example showing that: 1) I don't attempt to escape quotes within the same type of quote, 2) nor in alternative quotes, and 3) Perl is used to automate the process of generating a valid bash string containg imbedded quotes
– Adrian Pronk
May 12 '13 at 6:51
The first paragraph by itself is the answer I was looking for.
– Dave Causey
Mar 4 '16 at 15:06
This is what bash does as well, type
set -x
and echo "here's a string"
and you'll see that bash executes echo 'here'''s a string'
. (set +x
to return normal behavior)– arekolek
Aug 6 '16 at 23:45
set -x
echo "here's a string"
echo 'here'''s a string'
set +x
Since Bash 2.04 syntax $'string'
(instead of just 'string'
; warning: do not confuse with $('string')
) is another quoting mechanism which allows ANSI C-like escape sequences and do expansion to single-quoted version.
$'string'
'string'
$('string')
Simple example:
$> echo $'aa'bb'
aa'bb
$> alias myvar=$'aa'bb'
$> alias myvar
alias myvar='aa'''bb'
In your case:
$> alias rxvt=$'urxvt -fg '#111111' -bg '#111111''
$> alias rxvt
alias rxvt='urxvt -fg '''#111111''' -bg '''#111111''''
Common escaping sequences works as expected:
' single quote
" double quote
\ backslash
n new line
t horizontal tab
r carriage return
Below is copy+pasted related documentation from man bash
(version 4.4):
man bash
Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. Backslash escape sequences, if present, are decoded as follows:
a alert (bell)
b backspace
e
E an escape character
f form feed
n new line
r carriage return
t horizontal tab
v vertical tab
\ backslash
' single quote
" double quote
? question mark
nnn the eight-bit character whose value is the octal
value nnn (one to three digits)
xHH the eight-bit character whose value is the hexadecimal
value HH (one or two hex digits)
uHHHH the Unicode (ISO/IEC 10646) character whose value is
the hexadecimal value HHHH (one to four hex digits)
UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value
is the hexadecimal value HHHHHHHH (one to eight
hex digits)
cx a control-x character
The expanded result is single-quoted, as if the dollar sign had not been present.
See Quotes and escaping: ANSI C like strings on bash-hackers.org wiki for more details. Also note that "Bash Changes" file (overview here) mentions a lot for changes and bug fixes related to the $'string'
quoting mechanism.
$'string'
According to unix.stackexchange.com How to use a special character as a normal one? it should work (with some variations) in bash, zsh, mksh, ksh93 and FreeBSD and busybox sh.
could be used but the single quoted string here is not a real single quoted one, content on this string may be interprested by the shell:
echo $'foo'b!ar'
=> !ar': event not found
– regilero
May 28 '14 at 15:22
echo $'foo'b!ar'
!ar': event not found
On my machine
> echo $BASH_VERSION
4.2.47(1)-release
> echo $'foo'b!ar'
foo'b!ar
– mj41
Jun 9 '14 at 8:35
> echo $BASH_VERSION
4.2.47(1)-release
> echo $'foo'b!ar'
foo'b!ar
Yes, that's the reason for "may", I had it on a Red hat 6.4, certainly an older bash version.
– regilero
Jun 9 '14 at 11:46
Bash ChangeLog contains a lot of bug fixes related to
$'
so probably easiest way is to try it yourself on older systems.– mj41
Jun 14 '14 at 10:01
$'
also works for zsh thanks
– zzapper
Apr 29 '16 at 10:30
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 '
alias x='y 'z '
however, you can do this if you surround with double quotes:
alias x="echo 'y "
> x
> 'y
muffinresearch.co.uk/archives/2007/01/30/…
– cons
Aug 8 '09 at 23:10
Contents enclosed with double quotes are being evaluated so enclosing only single quotes in double quotes as suggested by liori seems to be proper solution.
– Piotr Dobrogost
Nov 16 '12 at 17:52
This is the actual answer to the question. While the accepted answer may provide a solution, it's technically answering a question that wasn't asked.
– Matthew G
May 14 '15 at 8:08
Matthew, the question was about escaping single quotes inside single quotes. This answer asks the user to change their behavior, and if you have an impediment to using double quotes (as the question title suggests), this answer wouldn't help. It's pretty useful though (Albeit obvious), and as such deserves an upvote, but the accepted answer addresses the precise problem Op asked about.
– Fernando Cordeiro
Jul 9 '15 at 21:24
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.
'''
'A '''B''' C'
A 'B' C
'''
'
'
This is misleading, this syntax ''' does not go "inside" a single quoted string. In this statement 'A '''B''' C' you are concatenating 5 escape and single quotes strings
– teknopaul
Jan 9 '16 at 23:18
@teknopaul The assignment
alias something='A '''B''' C'
does result in something
being a single string, so even while the right-hand side of the assignment is not technically a single string, I don't think it much matters.– Teemu Leisti
Sep 7 '16 at 7:01
alias something='A '''B''' C'
something
While this works in your example, it isn't technically providing a solution for how to insert a single quote inside a single quoted string. You've already explained it, but yes it's doing
'A ' + ' + 'B' + ' + ' C'
. In other words, a solution for inserting single quote characters inside a single-quoted string should allow me to create such a string by itself, and print it. However this solution will not work in this case. STR='''; echo $STR
. As designed, BASH does not truly allow this.– krb686
Sep 14 '16 at 2:07
'A ' + ' + 'B' + ' + ' C'
STR='''; echo $STR
@mikhail_b, yes,
'''
works for bash. Could you point out which sections of gnu.org/software/bash/manual/bashref.html specify such a behavior?– Jingguo Yao
Sep 16 '16 at 6:11
'''
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.
'
'
'
I'm not specifically addressing the quoting issue because, well, sometimes, it's just reasonable to consider an alternative approach.
rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }
which you can then call as:
rxvt 123456 654321
the idea being that you can now alias this without concern for quotes:
alias rxvt='rxvt 123456 654321'
or, if you need to include the #
in all calls for some reason:
#
rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }
which you can then call as:
rxvt '#123456' '#654321'
then, of course, an alias is:
alias rxvt="rxvt '#123456' '#654321'"
(oops, i guess i kind of did address the quoting :)
I was trying to put something within single quotes that was in double quotes which were, in turn, in single quotes. Yikes. Thank you for your answer of "try a different approach". That made the difference.
– Clinton Blackmore
Apr 28 '10 at 0:09
whoops, I made a scarf
– mgalgs
Jul 3 '12 at 16:59
I'm 5 years late, but aren't you missing a single quote in your last alias?
– Julien
Jan 19 '17 at 19:05
@Julien I don't see a problem ;-)
– nicerobot
Jan 20 '17 at 4:11
I just use shell codes.. e.g. x27
or \x22
as applicable. No hassle, ever really.
x27
\x22
Thank you for keeping it simple. This worked for me.
– Jared Beach
May 20 '16 at 2:06
Could you show an example of this in operation? For me it just prints a literal
x27
(on Centos 6.6)– Will Sheppard
Mar 28 at 10:37
x27
Yeap, just what I was searching for! Somehow in my case I couldn't use ' and x27 worked o/
– davi5e
May 3 at 22:06
Since one cannot put single quotes within single quoted strings, the simplest and most readable option is to use a HEREDOC string
command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)
alias rxvt=$command
In the code above, the HEREDOC is sent to the cat
command and the output of that is assigned to a variable via the command substitution notation $(..)
cat
$(..)
Putting a single quote around the HEREDOC is needed since it is within a $()
$()
I wish I'd scrolled down this far before - I reinvented this approach and came here to post it! This is far cleaner and more readable than all the other escaping approaches. Not it will not work on some non-bash shells, such as
dash
which is the default shell in Ubuntu upstart scripts and elsewhere.– Korny
Aug 10 '17 at 10:04
dash
Both versions are working, either with concatenation by using the escaped single quote character ('), or with concatenation by enclosing the single quote character within double quotes ("'").
The author of the question did not notice that there was an extra single quote (') at the end of his last escaping attempt:
alias rxvt='urxvt -fg'''#111111''' -bg '''#111111'''
│ │┊┊| │┊┊│ │┊┊│ │┊┊│
└─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
┊┊ ┊┊ ┊┊ ┊┊│
┊┊ ┊┊ ┊┊ ┊┊│
└┴─────────┴┴───┰───┴┴─────────┴┘│
All escaped single quotes │
│
?
As you can see in the previous nice piece of ASCII/Unicode art, the last escaped single quote (') is followed by an unnecessary single quote ('). Using a syntax-highlighter like the one present in Notepad++ can prove very helpful.
The same is true for another example like the following one:
alias rc='sed '"'"':a;N;$!ba;s/n/, /g'"'"
alias rc='sed ''':a;N;$!ba;s/n/, /g''
These two beautiful instances of aliases show in a very intricate and obfuscated way how a file can be lined down. That is, from a file with a lot of lines you get only one line with commas and spaces between the contents of the previous lines. In order to make sense of the previous comment, the following is an example:
$ cat Little_Commas.TXT
201737194
201802699
201835214
$ rc Little_Commas.TXT
201737194, 201802699, 201835214
Upwoted for the ASCII Table illustration :)
– php-dev
Jul 13 '15 at 6:24
Most of these answers hit on the specific case you're asking about. There is a general approach that a friend and I have developed that allows for arbitrary quoting in case you need to quote bash commands through multiple layers of shell expansion, e.g., through ssh, su -c
, bash -c
, etc. There is one core primitive you need, here in native bash:
su -c
bash -c
quote_args() {
local sq="'"
local dq='"'
local space=""
local arg
for arg; do
echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
space=" "
done
}
This does exactly what it says: it shell-quotes each argument individually (after bash expansion, of course):
$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'
It does the obvious thing for one layer of expansion:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(Note that the double quotes around $(quote_args ...)
are necessary to make the result into a single argument to bash -c
.) And it can be used more generally to quote properly through multiple layers of expansion:
$(quote_args ...)
bash -c
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
The above example:
quote_args
bash
-c
bash -c
That's the idea in a nutshell. You can do some pretty complicated stuff with this, but you have to be careful about order of evaluation and about which substrings are quoted. For instance, the following do the wrong things (for some definition of "wrong"):
$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure
In the first example, bash immediately expands quote_args cd /; pwd 1>&2
into two separate commands, quote_args cd /
and pwd 1>&2
, so the CWD is still /tmp
when the pwd
command is executed. The second example illustrates a similar problem for globbing. Indeed, the same basic problem occurs with all bash expansions. The problem here is that a command substitution isn't a function call: it's literally evaluating one bash script and using its output as part of another bash script.
quote_args cd /; pwd 1>&2
quote_args cd /
pwd 1>&2
/tmp
pwd
If you try to simply escape the shell operators, you'll fail because the resulting string passed to bash -c
is just a sequence of individually-quoted strings that aren't then interpreted as operators, which is easy to see if you echo the string that would have been passed to bash:
bash -c
$ (cd /tmp; echo "$(quote_args cd /; pwd 1>&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'
The problem here is that you're over-quoting. What you need is for the operators to be unquoted as input to the enclosing bash -c
, which means they need to be outside the $(quote_args ...)
command substitution.
bash -c
$(quote_args ...)
Consequently, what you need to do in the most general sense is to shell-quote each word of the command not intended to be expanded at the time of command substitution separately, and not apply any extra quoting to the shell operators:
$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success
Once you've done this, the entire string is fair game for further quoting to arbitrary levels of evaluation:
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success
etc.
These examples may seem overwrought given that words like success
, sbin
, and pwd
don't need to be shell-quoted, but the key point to remember when writing a script taking arbitrary input is that you want to quote everything you're not absolutely sure doesn't need quoting, because you never know when a user will throw in a Robert'; rm -rf /
.
success
sbin
pwd
Robert'; rm -rf /
To better understand what is going on under the covers, you can play around with two small helper functions:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
that will enumerate each argument to a command before executing it:
$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
Hi Kyle. Your solution worked great for a case I had, when I needed to pass a group of arguments as a single argument:
vagrant ssh -c {single-arg} guest
. The {single-arg}
needs to be treated as a single arg because vagrant takes the next arg after it as the guest name. The order cannot be changed. But I needed to pass a command and its arguments within {single-arg}
. So I used your quote_args()
to quote the command and its args, and put double quotes around the result, and it worked like a charm: vagrant ssh -c "'command' 'arg 1 with blanks' 'arg 2'" guest
. Thanks!!!– Andreas Maier
Feb 5 '16 at 23:33
vagrant ssh -c {single-arg} guest
{single-arg}
{single-arg}
quote_args()
vagrant ssh -c "'command' 'arg 1 with blanks' 'arg 2'" guest
Thanks for the feedback. Glad it helped!
– Kyle Rose
Mar 15 '16 at 18:57
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.
alias
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"
"real answer is that you can't escape single-quotes within single-quoted strings." That's technically true. But you can have a solution that starts with a single quote, ends with a single quote, and only contains single quotes in the middle. stackoverflow.com/a/49063038
– wisbucky
Mar 2 at 4:58
Not by escaping, only by concatenation.
– teknopaul
Mar 3 at 17:02
In the given example, simply used double quotes instead of single quotes as outer escape mechanism:
alias rxvt="urxvt -fg '#111111' -bg '#111111'"
This approach is suited for many cases where you just want to pass a fixed string to a command: Just check how the shell will interpret the double-quoted string through an echo
, and escape characters with backslash if necessary.
echo
In the example, you'd see that double quotes are sufficient to protect the string:
$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'
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
see the shell code answer (Rob Jens)
– PythoNic
Jun 8 '16 at 17:08
Another way to fix the problem of too many layers of nested quotation:
You are trying to cram too much into too tiny a space, so use a bash function.
The problem is you are trying to have too many levels of nesting, and the basic alias technology is not powerful enough to accommodate. Use a bash function like this to make it so the single, double quotes back ticks and passed in parameters are all handled normally as we would expect:
lets_do_some_stuff() {
tmp=$1 #keep a passed in parameter.
run_your_program $@ #use all your passed parameters.
echo -e 'n-------------' #use your single quotes.
echo `date` #use your back ticks.
echo -e "n-------------" #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff
Then you can use your $1 and $2 variables and single, double quotes and back ticks without worrying about the alias function wrecking their integrity.
This program prints:
el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------
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.
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
.
alias
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.
foo='a''b'
foo='ab'
'
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.)
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!'
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Do you realise that you don't need to use single quotes for alias? Double quotes is much easier.
– teknopaul
Jan 9 '16 at 23:58