Nesting quotes in shell

I want to execute a command which looks like

$ sh -c 'exec python -c "print('$1$2MyString')"'

with the desired result '$1$2MyString'.

However, when I execute the command, I get

Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name 'MysString' is not defined

I understand that when I add a third level of quotes, it essentially cuts the command in two parts, and that the shell sees $1$2MyString outside quotes and tries to substitute for known values.

If I escape the single quotes,

$ sh -c 'exec python -c "print('$1$2MyString')"'

I get the following error:

sh: 1: Syntax error: "(" unexpected

Essentially, my question is: is there a way to introduce string literals in the print function above?

Asked By: johnc

||

You can’t escape single quotes inside a single quoted string. For example this is an incomplete line:

echo 'boy's game'    # Wrong!
>

However you can abut strings enclosed by different types of quotes:

echo "boy's"' game'    # Right
boy's game

As a consequence you can quote the quotes, "'" etc.:

sh -c 'exec python -c "print('"'"'$1$2MyString'"'"')"'
#     ^-. . . . . . . . . . -^   ^-. . . . . -^   ^--^
#                             ^-^              ^-^

This outputs MyString because the $1$2 are evaluated by the invocation of sh. If you want $1$2MyString as a literal you need to ensure it’s inside single quotes and protected from the explicit sh:

sh -c 'exec python -c '"'"'print("$1$2MyString")'"'"
#     ^-. . . . . . .-^   ^-. . . . . . . . . .-^
Answered By: Chris Davies

Quotes never nest (apart from command substitutions): Within a single-quoted string, the double-quote is a regular character, and doesn’t act as a quote. Similarly the other way around.

If you want to pass to python the argument print('$1$2MyString'), you can do the necessary escaping, which is made awkward by the fact that the string contains both single quotes and dollar signs, meaning neither single nor double quotes can be used without further escaping. Even for that string itself, let alone considering the second level of quoting required.

Or, do something like passing the string through the environment to sh:

code="print('$1$2MyString')"  # or
code='print('''$1$2MyString''')'

code=$code sh -c 'exec python -c "$code"'

or, if the output was supposed to be '$1$2MyString', with the single quotes, then the Python code needs to be print("'$1$2MyString'"):

code='print("'''$1$2MyString'''")'
code=$code sh -c 'exec python -c "$code"'

It might be simpler to just put the Python code in a file.

Answered By: ilkkachu
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.