5

Just thought I'd document this: I'm trying something very simple - set env variable in bash, and print it out:

$ bash -c "a=1; echo a$a;"
a
$ bash -c "a=1; echo a\$a;"
a1

Now I'd want this same thing, but called as argument of sh (on my system, ls -la $(which sh) gives /bin/sh -> dash):

$ sh -c "bash -c "a=1; echo a\$a;""
a$a

# obviously I have to escape inner quotes:

$ sh -c "bash -c \"a=1; echo a\$a;\""
a

# escape the dollar once more?

$ sh -c "bash -c \"a=1; echo a\\$a\" "
sh: Syntax error: Unterminated quoted string

# nope... inner single quotes, then?

$ sh -c "bash -c 'a=1; echo a$a;'"
a

# nope... escape the single quotes?

$ sh -c "bash -c \'a=1; echo a$a;\'"
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
a
sh: ': not found

# nope... escape the dollar too?

$ sh -c "bash -c \'a=1; echo a\$a;\'"
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
a
sh: ': not found

So, my question would be - what is the proper syntax for escaping, so that sh -c [bash -c ...] gives the same result as just bash -c ...?

sdaau
  • 6,778

2 Answers2

2

Inside single quotes, no character has a special meaning. Inside double quotes, "\$` have a special meaning. Work it out from the outside in: first figure out what string is built by the outer shell, then what the inner shell will make of it.

For example, supposing that the variable a is not defined in the outer shell, with

sh -c "bash -c \"a=1; echo a\$a;\""

the outer shell sees a double-quoted string which expands to bash -c "a=1; echo a$a;", and it's that string which is passed to the intermediate sh. sh parses this as a call to bash with the two arguments -c and a=1; echo a;, the latter resulting from expanding the double-quoted string "a=1; echo a$a;" where the variable a is not defined.

If you do this analysis you can see that one way of getting what you want would be to have "a=1; echo a\$a;" at this stage. To get this extra backslash, you need to put two backslashes in the original, since there has already been one stage of shell expansion inside double quotes. Two plus one is three.

sh -c "bash -c \"a=1; echo a\\\$a;\""

It would be simpler to use single quotes for the outer string, since you don't want to expand anything in there.

sh -c 'bash -c "a=1; echo a\$a;"'

Since you don't want to expand anything when you call bash from sh either, you could use single quotes there as well. You can't put single quotes inside a single-quoted string, but there's a way to simulate this: put '\''. Formally this terminates the single-quoted literal, appends a literal single quote, and starts a new single-quoted literal, but you can think of the four-character sequence '\'' as being a way to put a single quote inside a single-quoted string.

sh -c 'bash -c '\''a=1; echo a$a;'\'''

You can omit that '' at the end, it's less systematic but easier on the eyes.

sh -c 'bash -c '\''a=1; echo a$a;'\'

Single quotes are not special inside double quotes, so when you write "bash -c 'a=1; echo a\$a;'" you need the backslash before the dollar to prevent the expansion of $a in the outer shell.

1

Well, I found a couple that do work (NB, this is Ubuntu 11.04):

$ sh -c "bash -c 'a=1; echo a\$a \'"
a1
$ sh -c "bash -c 'a=1; echo a\$a;'"
a1

... and it seems, as long as the first single quote is unescaped, and the dollar is escaped - it works, regardless if the final single quote is escaped or not! Which puzzles me quite a bit still...

sdaau
  • 6,778
  • sh -c "bash -c 'a=1; echo a\$a \'" works by happenstance, but it's a bit bizarre. The final single quote is not escaped: \' retains the backslash inside double quotes. The command passed to bash is a=1; echo a$a \, and that final backslash happens to have no effect. – Gilles 'SO- stop being evil' Apr 24 '13 at 01:22