Have shlex.quote()'d shell arguments nestable

shlex.quote() quotes shell arguments by generally using single-quote and sometimes double-quote quoting:

>>> print(shlex.quote("File name, '.odg"))
'File name, '"'"'.odg'

But arguments quoted such cannot be nested within other arguments; as an example, an f-string

do-notification {shlex.quote(text)} --action 'echo {shlex.quote(text2)}'

would be converted to

do-notification 'text' --action 'echo 'File name, '"'"'.odg''

But backslash-style quoting does not have that downside:

do-notification 'text' --action 'echo File\ name\,\ \'.odg'

I propose either to change behavior or to have some kind of option to quote arguments as such so they can be nested.

What if you call quote() twice? --action {shlex.quote('echo ' + shlex.quote(text2))}

3 Likes

Another solution is to always place the output of shlex.quote into unquoted context:

--action 'echo '{shlex.quote(text2)}  # don't do this, see below

On a second thought, double-quoting as suggested by the first solution above is the way to go, since you’re passing text2 as the argument to a command which will be interpreted by another shell, so you would be vulnerable to injection attacks like text2='farewell; rm -rf /'. I.e., my “solution” here is analogous to passing an "echo $text2" command as the --action, while the double-quoting solution above is analogous to passing "echo $(printf '%q' "$text2")" as the --action:

$ text2='farewell; rm -rf'
$ command="echo $text2"
$ echo "$command"
echo farewell; rm -rf
$ command="echo $(printf '%q' "$text2")"
$ echo "$command"
echo farewell\;\ rm\ -rf

Classic of me to disappear somewhere for months.
Yes, thank you, this is the way to go. Even the suggestion to add escape-style quoting as an alternative isn’t gonna make it since there is no way to quote zero-length string. Should’ve thought out it a little more to not have missed an obvious trivial case.