404

How can I escape double quotes inside a double string in Bash?

For example, in my shell script

#!/bin/bash

dbload="load data local infile \"'gfpoint.csv'\" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY \"'\n'\" IGNORE 1 LINES"

I can't get the ENCLOSED BY '\"' with double quote to escape correctly. I can't use single quotes for my variable, because I want to use variable $dbtable.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sean Nguyen
  • 12,528
  • 22
  • 74
  • 113
  • 1
    Also see http://mywiki.wooledge.org/BashFAQ/050 – Charles Duffy Nov 13 '13 at 13:38
  • possible duplicate of [Escaping single-quotes within single-quoted strings](http://stackoverflow.com/questions/1250079/escaping-single-quotes-within-single-quoted-strings) – kenorb Feb 28 '15 at 20:50
  • 5
    @kenorb Doesn't look like a duplicate of that question... – Mark Oct 26 '16 at 08:41
  • See also http://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable – tripleee Mar 16 '17 at 06:44
  • @Daenyth This isn't the type of command you'd expect end users to have any access to. Bulk load scripts are usually run *on* the server by trusted users (such as system admins or developers). Yes, if end users control the value of `$dbtable`, there's a risk. This would be *very* uncommon, though, as end users don't typically SSH into a machine to load their data. – jpmc26 Dec 19 '17 at 23:43
  • @jpmc26 Malicious input isn't the only case - loading from a csv could easily include data that's invalid sql just by accident. – Daenyth Dec 20 '17 at 00:49
  • @Daenyth The CSV is never parsed as SQL.... It's parsed by an explicit [CSV parsing command](https://dev.mysql.com/doc/refman/5.7/en/load-data.html) that inserts the values into a table. – jpmc26 Dec 20 '17 at 01:12
  • See also: [Difference between single and double quotes in Bash](https://stackoverflow.com/questions/6697753/difference-between-single-and-double-quotes-in-bash). – codeforester Jan 17 '18 at 04:25
  • The canonical for single quotes: *[How to escape single quotes within single quoted strings](https://stackoverflow.com/questions/1250079/)* – Peter Mortensen Aug 16 '23 at 19:24

10 Answers10

379

Use a backslash:

echo "\""     # Prints one " character.
Peter
  • 127,331
  • 53
  • 180
  • 211
  • 16
    Not working. `x=ls; if [ -f "$(which "\""$x"\"")" ]; then echo exists; else echo broken; fi;` gives broken whereas `... [ -f "$(which $x)" ]; ...` or `... [ -f $(which "$x") ]; ...` work just fine. Issues would arise when either `$x` or the result of `$(which "$x")` gives anything with a space or other special character. A workaround is using a variable to hold the result of `which`, but is bash really incapable of escaping a quote or am I doing something wrong? – Luc Jul 27 '15 at 15:10
  • I am trying to use the following `grep -oh "\"\""$counter"\""\w*" ` as part of a bash syntax where in `$counter` is a variable. it doesn't like it any thoughts – Jay D Dec 18 '15 at 00:30
  • 4
    Hi, your method doesn't work. Please delete your answer as it might have already wasted so much useful time. The correct answer is posted by @kenorb your answer only work while terminal invocation but not with bash file. Did you even try this yourself before posting(probably a cut paste solution) here :( ? – AndroidEngineX Nov 13 '21 at 15:02
  • @AndroidEngineX, this method works fine when used correctly. Luc's comment above is using it wrong; they're telling `which` to search for an executable that has literal quotes in its name, and of course no such thing exists. That's Luc's own problem, not a mistake with this answer. – Charles Duffy Sep 07 '22 at 21:21
  • @JayD, `\w` doesn't work in POSIX-standard `grep` at all in the first place. It's a PCRE extension, and grep only supports BRE and ERE. I couldn't evaluate your comment more than that without knowing what strings you expect to match / how you're testing. – Charles Duffy Sep 07 '22 at 21:22
142

A simple example of escaping quotes in the shell:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

It's done by finishing an already-opened one ('), placing the escaped one (\'), and then opening another one (').

Alternatively:

$ echo 'abc'"'"'abc'
abc'abc
$ echo "abc"'"'"abc"
abc"abc

It's done by finishing already opened one ('), placing a quote in another quote ("'"), and then opening another one (').

More examples: Escaping single-quotes within single-quoted strings

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kenorb
  • 155,785
  • 88
  • 678
  • 743
  • 1
    I tried sh -c "echo '{"key":"value"}'" and even sh -c "echo '{''"''key''"'':''"''value''"''}'" in an effort to enclose the words key and value in double quotes, but in both cases I got {key:value} – Igor Yagolnitser Jun 18 '17 at 09:28
  • 2
    This seems unnecessarily complicated for double quotes: `echo "abc\"abc"` is sufficient to produce `abc"abc` as in Peter answer. – divenex Oct 19 '18 at 15:33
  • 4
    In this simple example indeed, but in complex cases of nested quotes, it can be necessary to do this and @kenorb's example helped me figure out how to deal with those cases. – prosoitos May 03 '19 at 03:58
90

Keep in mind that you can avoid escaping by using ASCII codes of the characters you need to echo.

Example:

echo -e "This is \x22\x27\x22\x27\x22text\x22\x27\x22\x27\x22"
This is "'"'"text"'"'"

\x22 is the ASCII code (in hex) for double quotes and \x27 for single quotes. Similarly you can echo any character.

I suppose if we try to echo the above string with backslashes, we will need a messy two rows backslashed echo... :)

For variable assignment this is the equivalent:

a=$'This is \x22text\x22'
echo "$a"

# Output:
This is "text"

If the variable is already set by another program, you can still apply double/single quotes with sed or similar tools.

Example:

b="Just another text here"
echo "$b"

 Just another text here

sed 's/text/"'\0'"/' <<<"$b" #\0 is a special sed operator
 Just another "0" here #this is not what i wanted to be

sed 's/text/\x22\x27\0\x27\x22/' <<<"$b"

 Just another "'text'" here #now we are talking. You would normally need a dozen of backslashes to achieve the same result in the normal way.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
George Vasiliou
  • 6,130
  • 2
  • 20
  • 27
  • 1
    +1 because it solved a problem of adding a PS1 variable to ~/.profile `echo 'export PS1='\[\033[00;31m\]${?##0}$([ $? -ne 0 ] && echo \x22 \x22)\[\033[00;32m\]\u\[\033[00m\]@\[\033[00;36m\]\h\[\033[00m\][\[\033[01;33m\]\d \t\[\033[00m\]] \[\033[01;34m\]\w\n\[\033[00m\]$( [ ${EUID} -ne 0 ] && echo \x22$\x22 || echo \x22#\x22 ) '' >> ~/.profile` – Yassine ElBadaoui Apr 18 '17 at 05:19
  • 2
    This is THE answer! I love u Sir. – Miguel Rojas Cortés Nov 17 '19 at 22:22
37

Bash allows you to place strings adjacently, and they'll just end up being glued together.

So this:

echo "Hello"', world!'

produces

Hello, world!

The trick is to alternate between single and double-quoted strings as required. Unfortunately, it quickly gets very messy. For example:

echo "I like to use" '"double quotes"' "sometimes"

produces

I like to use "double quotes" sometimes

In your example, I would do it something like this:

dbtable=example
dbload='load data local infile "'"'gfpoint.csv'"'" into '"table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"'"'"' LINES "'TERMINATED BY "'"'\n'"'" IGNORE 1 LINES'
echo $dbload

which produces the following output:

load data local infile "'gfpoint.csv'" into table example FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "'\n'" IGNORE 1 LINES

It's difficult to see what's going on here, but I can annotate it using Unicode quotes. The following won't work in Bash – it's just for illustration:

dbload=load data local infile "’“'gfpoint.csv'”‘" into ’“table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '”‘"’“' LINES ”‘TERMINATED BY "’“'\n'”‘" IGNORE 1 LINES

The quotes like “ ‘ ’ ” in the above will be interpreted by bash. The quotes like " ' will end up in the resulting variable.

If I give the same treatment to the earlier example, it looks like this:

echo I like to use "double quotes" sometimes

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Beetle
  • 1,959
  • 16
  • 32
  • a subtlety of my second `echo` example, that I've not bothered to explain, is that here I'm actually giving echo three separate arguments, because the strings aren't touching. but the `echo` command prints all three, separated by spaces, so it sort of does what you'd expect. The difference is, if you type `echo a b c` it will output `a b c`, whereas if you type `echo 'a b c'` it will print `a b c`. – Beetle May 09 '22 at 10:56
33

Store the double quote character in a variable:

dqt='"'
echo "Double quotes ${dqt}X${dqt} inside a double quoted string"

Output:

Double quotes "X" inside a double quoted string
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
12oclocker
  • 409
  • 4
  • 5
20

Check out printf...

#!/bin/bash
mystr="say \"hi\""

Without using printf

echo -e $mystr

Output: say "hi"

Using printf

echo -e $(printf '%q' $mystr)

Output: say \"hi\"

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Danny Hong
  • 1,474
  • 13
  • 21
  • 2
    Note that `printf` escapes more characters as well, such as `'`, `(` and `)` – David Pärsson May 10 '13 at 10:14
  • `printf %q` generates strings ready for `eval`, not formatted for `echo -e`. – Charles Duffy Nov 13 '13 at 13:38
  • 3
    There is no reason to wrap the `printf` with a [useless use of `echo`](http://www.iki.fi/era/unix/award.html#echo). Both your examples have broken quoting. The proper fix is to double-quote the variable. – tripleee Mar 16 '17 at 06:33
12

Make use of $"string".

In this example, it would be,

dbload=$"load data local infile \"'gfpoint.csv'\" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY \"'\n'\" IGNORE 1 LINES"

Note (from the man page):

A double-quoted string preceded by a dollar sign ($"string") will cause the string to be translated according to the current locale. If the current locale is C or POSIX, the dollar sign is ignored. If the string is translated and replaced, the replacement is double-quoted.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
2

For use with variables that might contain spaces in you Bash script, use triple quotes inside the main quote, e.g.:

[ "$(date -r """$touchfile""" +%Y%m%d)" -eq "$(date +%Y%m%d)" ]
Majal
  • 1,635
  • 20
  • 31
  • 1
    Triple quotes have no special meaning in bash: two of the quotes cancel each other out, leaving only the third; making the behavior 100% identical to just using only one quote. (`$( )` creates a new quoting context, so you don't **need** to escape double quotes inside it at all in the first place; it's only backticks that have a problem there). – Charles Duffy Sep 07 '22 at 21:25
  • 1
    Try it yourself: `[ "$(date -r "$touchfile" +%Y%m%d)" -eq "$(date +%Y%m%d)" ]` works exactly the same way as the code suggested in this answer. – Charles Duffy Sep 07 '22 at 21:26
0

How about use echo.

For example,

x=$(echo \"a b c\")

To use the variable,

echo $x && ls "$x"

The reason I prefer echo rather than printf is that it's more straightforward.

Yisu Peng
  • 154
  • 1
  • 5
-9

Add "\" before double quote to escape it, instead of \

#! /bin/csh -f

set dbtable = balabala

set dbload = "load data local infile "\""'gfpoint.csv'"\"" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"\""' LINES TERMINATED BY "\""'\n'"\"" IGNORE 1 LINES"

echo $dbload
# load data local infile "'gfpoint.csv'" into table balabala FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "''" IGNORE 1 LINES
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shilv
  • 9
  • 2
  • 10
    Downvote: Why are you posting a `csh` answer to a `bash` question? The two are completely distinct and incompatible. – tripleee Mar 16 '17 at 06:35