How to escape single quotes within single quoted strings
Solution 1
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 '
:
-
'
End first quotation which uses single quotes. -
"
Start second quotation, using double-quotes. -
'
Quoted character. -
"
End second quotation, using double-quotes. -
'
Start third quotation, using single quotes.
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.
Solution 2
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!
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'\''\'\'''\'''\'''
Solution 3
Since Bash 2.04 syntax $'string'
allows a limit set of escapes.
Since Bash 4.4, $'string'
also allows the full set of C-style escapes, making the behavior differ slightly in $'string'
in previous versions. (Previously the $('string')
form could be used.)
Simple example in Bash 2.04 and newer:
$> 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):
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.
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.
Solution 4
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
Solution 5
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.
cons
Updated on July 11, 2022Comments
-
cons almost 2 years
Let's say, you have a Bash
alias
like: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.
-
cons over 14 years
-
Jacob over 14 yearsBut 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.
-
Clinton Blackmore about 14 yearsI 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.
-
Uphill_ What '1 almost 13 years
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! -
Piotr Dobrogost over 11 yearsContents enclosed with double quotes are being evaluated so enclosing only single quotes in double quotes as suggested by liori seems to be proper solution.
-
Adrian Pronk almost 11 years@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
-
Gorm Casper over 10 yearsMy interpretation: bash implicitly concatenates differently quoted string expressions.
-
JAMESSTONEco over 10 yearsworked 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'"'"''"'"'}"'
-
oberlies over 10 yearsCertainly not the most readable solution. It over-uses single quotes where they are not really needed.
-
Pyrolistical about 10 yearsThis is a nice solution, since it avoids the hard problem of reworking the quotes for the command, which requires thinking. This solution just requires a simple translation. If only bash had macros, the you could implement this.
-
2rs2ts about 10 yearsDoes this work when you swap the roles of the double and single quotes? i.e.
"urxvt -fg "'"'"#111111"'"'"
etc? Doesn't seem to be working for me but maybe I'm doing something wrong. -
liori about 10 years@2rs2ts: it should work. If not—please make a new question here on the site.
-
regilero almost 10 yearscould 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
-
user14922101 almost 10 yearsOn my machine
> echo $BASH_VERSION
4.2.47(1)-release
> echo $'foo\'b!ar'
foo'b!ar
-
regilero almost 10 yearsYes, that's the reason for "may", I had it on a Red hat 6.4, certainly an older bash version.
-
user14922101 almost 10 yearsBash ChangeLog contains a lot of bug fixes related to
$'
so probably easiest way is to try it yourself on older systems. -
juanmf almost 10 yearsNice! I was having trouble with an alias in .bash_aliases I did as you suggest and then issuing alias in CLI, outputs
... awk '\''{print $3}'\''
'` i.e. it's also valid\'
instead of"'"
outside single quote string. -
Chad Skeeters almost 10 years@rs2ts Leave off the last double quote.
echo "it "'"'"works"'"'
-
karmakaze over 9 yearsTL;DR replace each internal
'
with'\''
command = "echo '" + arg.replace("'", "'\\''") + "'";
exec(command)
-
Steve Midgley over 9 yearsGreat answer content-wise. But it would be easier to understand if it were possible to concatenate with a symbol like + -- that's not possible (afaik) but an example showing what that looks like might help others grok the One True Way (this example doesn't work it's to help understand what the hey is going on):
alias rxvt='urxvt -fg '+"'"+'#111111'+"'"+' -bg '+"'"+'#111111'+"'"
-
cmroanirgo over 9 yearsFor those who just want the solution (where double is ok): alias rxvt="urxvt -fg '#111111' -bg '#111111'"
-
Matthew G almost 9 yearsThis 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.
-
Fernando Cordeiro almost 9 yearsMatthew, 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.
-
Alex Gray over 8 yearsfor those of you that REALLY want the answer, lol... just use
'"'"'
. and if you want to escape THOSE single quotes, use'\"'\"'
LOL -
teknopaul over 8 yearsThis 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 over 8 yearsThis is wrong "single quotes in the outermost layer" is not what's being achieved, you are concatenating strings using different escape types. There is no outermost layer.
-
Andreas Maier over 8 yearsHi 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 yourquote_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!!! -
Dave Causey about 8 yearsThe first paragraph by itself is the answer I was looking for.
-
arekolek almost 8 yearsThis is what bash does as well, type
set -x
andecho "here's a string"
and you'll see that bash executesecho 'here'\''s a string'
. (set +x
to return normal behavior) -
mtraceur over 7 yearsI 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. -
Teemu Leisti over 7 years@teknopaul The assignment
alias something='A '\''B'\'' C'
does result insomething
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. -
krb686 over 7 yearsWhile 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. -
Jingguo Yao over 7 years@mikhail_b, yes,
'\''
works for bash. Could you point out which sections of gnu.org/software/bash/manual/bashref.html specify such a behavior? -
Ligemer over 7 yearsAnother example of usage:
awk '{print "'"'"'"$2"'"'"' => '"'"'"$3"'"'"'"}'
outputs a string such as:'value$2' => 'value$3'
-
Julien over 7 yearsI'm 5 years late, but aren't you missing a single quote in your last alias?
-
Jacob over 7 years@Julien I don't see a problem ;-)
-
Korny over 6 yearsI 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. -
Amil Waduwawara over 6 yearsDouble quotations can also be escaped in the same way; simply interchange double and single quotations in
'"'"'
-
wisbucky about 6 years"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 about 6 yearsLOL, using double quotes is cheating. :) Then you might as well just use double quotes on the outside for the simplest common sense solution. A real solution using only single quotes is stackoverflow.com/a/49063038
-
teknopaul about 6 yearsNot by escaping, only by concatenation.
-
Will Sheppard about 6 yearsCould you show an example of this in operation? For me it just prints a literal
x27
(on Centos 6.6) -
stason almost 6 years@liori, that's an awesome trick. Thank you! Now I'm able to mess with single quotes from within perl on-liners! a demo:
perl -le '$q = chr(39); $_=shift; s/$q/$q"$q"$q/g; print qq[echo $q$_$q]' "It's amazing"
gives:echo 'It'"'"'s amazing'
I added it to my recipes cookbook! -
stiller_leser almost 6 yearsbe aware:
e. Bash no longer inhibits C-style escape processing ($'...') while performing pattern substitution word expansions.
Taken from tiswww.case.edu/php/chet/bash/CHANGES. Still works in 4.3.42 but not in 4.3.48. -
Jonathan Cross almost 6 yearsOf the options below, this is the only one that works in my situation:
alias kernel_ext='kextstat -kl | awk '"'"'!/com\.apple/{printf "%s %s\n", $6, $7}'"'"
-
Matthew D. Scholefield almost 6 yearsNo need to quote a single quote in a double quote string.
-
Igor Tverdovskiy almost 4 yearsThank you! that what I looked for, the way to define a command as is via heredoc and pass the auto escaped command to ssh. BTW cat <<COMMAND without quotes allows to interpolate vatiables inside command and works as well for this approach.
-
João Ciocca almost 4 yearsthis is THE ONLY way I got to make a
grep
alias to work correctly! -
mtraceur almost 4 years+1 for using the
'\''
method I recommend over the'"'"'
method which is often harder for humans to read. -
alecxs almost 4 yearsuse
'$'\047''
or'$'\\047''
as replacement for'\''
depending on shell -
Marcos almost 4 years@WillSheppard
echo -e "\x27 \\x22"
prints' "
-
ojdo over 3 yearsThanks, this just worked perfectly for creating an alias containing single quotes, backslashes and a dollar sign without any manual fiddling on my side:
print(shlex_quote(r"..<nasty string>..."))
-
Gabriel Staples over 3 years@WillSheppard and others, here are a bunch of examples of this I just wrote: stackoverflow.com/a/65878993/4561887.
-
cokedude almost 3 yearsCan you help with this? Not sure how to deal with the
!
point here.ssh server "awk 'del=(a&&a--) {print; da=\!a} $0~pattern{if (da) {print "--"; da=0} a=A; if (B) {for(i=NR; i<B+NR; i++) if((i%B) in b) print b[i%B]} {print; da=1}} (B) {if (del) delete b[NR%B]; else b[NR%B]=$0}' B=5 A=2 pattern=Successful file"
-
Gabriel Staples almost 3 years@cokedude, try asking a new question. Paste a link to your new question here so I can help you on your question.
-
cokedude almost 3 yearsIs it better to post in the regular stackoverlow or the Unix stackoverflow?
-
Gabriel Staples almost 3 years@cokedude, either is fine, I think. I'd probably just do regular StackOverflow. But, be sure to describe your problem in detail, ensure that what you post is runnable by anyone. Explain what you did, what output you saw, and what you expected to see or wanted to happen instead. Even if you do all that and get it perfect, expect some downvotes. Be sure to search for existing questions which already answer it before posting. If your question lasts longer than 10 minutes before getting closed, consider it a success. That's just the nature of this site, unfortunately.
-
Jose V over 2 yearsThis does bring in all the C-style sequences into your bash line, so certain character sequences that work fine on bash might stop working as expected because they become C-style sequences. Typically easy to solve by adding extra
\
to escape the C-style sequences. Example:alias foo=$'echo \1'
is different thanalias boo='echo \1'
-
Kellen Stuart over 2 yearsThis is horrible. How does a language based on manipulating strings not have a clean string interpolation function built in?
-
Hola Soy Edu Feliz Navidad about 2 yearsThe worst thing in regards to your answer is that you are 100% right.
-
pyrocrasty about 2 years@MatthewD.Scholefield: it needs to be quoted because it's in an alias. There won't be any double quotes when the alias is expanded. (What is unnecessay is the space at the end, though).