Hacking around a hack

So google friendly search terms

Pexpect, subprocess.POpen. How to suppress quotes from Popen with Windows. Answer: You cannot BUT you can hack around it.

Note I am using delegator.py BUT it is just a very nice wrapper around PExpect which in turn is a really nice wrapper around Python’s subprocess.Popen which is a really nice wrapper around a ball of shit called CreateProcess. Pedants asside, you can’t make a ball of shit shine no matter how much abstraction you throw at it.

Solution:

fix = f"some_exec -arg abc -arg 123 -arg3=foo"
cmd_line = ["cmd", "/c", fix ]
result = delegator.run(cmd_line)

 

This looks goofy and it is. The problem I ran into is with a POSIX compliant app that wasn’t prepared for Windows compliant shenanigans https://docs.python.org/3/library/subprocess.html#converting-argument-sequence

Illustration

$some_exec -arg abc -arg2 123 -arg3=foo

That’s what I expected of crafting a subprocess call with an argument like

delegator.run(["some_exec", "-arg abc", "-arg 123", "-arg3=foo"])

What I got was

"some_exec", "-arg abc", "-arg 123", "-arg3=foo"

Which is hilarious because `some_exec` was so not prepared for that and wasn’t able to parse the command line arguments. How we go from a list of command arguments to a quoted text string is all thanks to this https://github.com/python/cpython/blob/3.7/Lib/subprocess.py#L485

Unless you want to write your own standard library version of subprocess.py just so you can work around one single (likely many) “non-complaint” Windows console executables, you need to abuse the cmd.exe shell’s own quirks.

To demonstrate what I mean

    fix = f"some_exec -arg abc -arg 123 -arg3=foo"
    cmd_line = ["echo", "cmd", "/c",  fix ]
    result = delegator.run(cmd_line)

Outputs `cmd /c “some_exec -arg abc -arg2 123 -arg3=foo` which is in turn executed by cmd.exe without quoting every single argument to `some_exec`.

I walked downward from delegator.py’s run() through pexpect to get to subprocess.py’s POpen and this is it, there wasn’t a cheaper fix then this.

For more info on why this works – tldr “cmd /c argument” is run inside of its own temporary shell and all of that sanitizing code in subprocess is skipped because it see’s the `fix` part of the sequence as one gigantic argument to `cmd`. If you try it any other way, say you try to skip out on cmd, you will get `some_exec “-arg abc -arg2 123 -arg3=foo”` which put me right BACK to having my some_exec blow up as it doesn’t know to strip the quotes out (or even to look inside for the arguments).

Summary: I didn’t log a bug report to some_exec’s maintainer because I needed this to work right the hell now and I didn’t have the time or energy to push through a bug report, wait for a fix, and I don’t have time to even wade into some_exec’s code base to figure out what to patch. Nevermind if it is written in C or C++ which I could do but I haven’t used either language in about a year so what I did write would be some ghastly buffer overflow exploit cesspool. I don’t have time to do that. Besides honestly if I was the developer/maintainer of some_exec I would be skeptical if I even wanted to deal with this bullshit on top of everything else that is more pressing.