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?
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")'"'"
# ^-. . . . . . .-^ ^-. . . . . . . . . .-^
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.