sh
sh copied to clipboard
Nested quotes not visible in stringification
I'm using sh.py to run commands in docker containers:
>>> container = sh.docker.bake(CONTAINER_NAME).bash.bake('-c')
# example usage -- works correctly
# executes the bash equivalent of "/usr/bin/docker exec mycontainer bash -c 'ls -l foo bar baz'"
>>> container("ls -l foo bar baz")
# this does the same, but leaves me an object I can use for introspection and logging
>>> baked = container.bake("ls -l foo bar baz")
>>> baked() # executes correctly
# example stringification of the baked command
str(baked)
"/usr/bin/docker exec mycontainer bash -c ls -l foo bar baz"
# ..so
"/usr/bin/docker exec mycontainer bash -c 'ls -l foo bar baz'" # execution
"/usr/bin/docker exec mycontainer bash -c ls -l foo bar baz" # stringification
Is it reasonably possible to change this to be more accurate, for the purposes of logging what commands are being used?
Thanks for your development of this library, btw, it's probably my favorite subprocess tool -- and thanks for your time looking at this issue.
As a side note, since bash requires the actual command given to '-c' to follow as a single argument, I can't use sh.py as though it had access to the docker container directly. This is, overall, acceptable, though, because I can still use sh.py for the job of making things easier to reference. I'm not sure if this is a library limitation or my limited understanding of sh.py. Care to enlighten me?
# side note example
# Possible
container = sh.docker.bake(CONTAINER_NAME).bash.bake('-c')
container("ls -l foo bar baz")
# Not Possible, unless there's a way I don't know
# example 1
container.ls('-l', 'foo', 'bar', 'baz')
# example 2
container_ll = container.ls.bake('-l')
container_ll('foo', 'bar', 'baz')
Again, that last code snippet is just an aside.
Regarding your side note, you're effectively constructing a command that is a string argument to another command. You could do that with the tools that are already available:
import ls
container = sh.docker.bake("exec", "my_container", "bash", "-c")
cmd = sh.ls.bake("-l", "foo", "bar", "baz")
container(str(cmd))
Regarding the original question, we would need to create a function that identifies if an argument requires quoting at all, since you probably don't want the str() output to be 'bash' '-c' 'ls ...' even though that's valid syntax. Plus, we'd need to escape existing single quotes correctly when printing quoted arguments.
There's a pretty good explanation of single-quoted bash variables rules at https://tldp.org/LDP/abs/html/quotingvar.html.
I want the problem to be as simple as "if there are spaces in this single word argument, quote it", but knowing my luck there's some other kind of chicanery.
also, it might be worth mentioning that, if I'm reading it right, the container(str(subcommand))() method wouldn't work if subcommand itself had a multiword argument, because that also wouldn't be quoted, due to the str(command_with_multiword_arg) issue.