Issue with subprocess.Popen and executing ssh comm

2019-06-03 07:09发布

问题:

I am using subprocess.Popen to execute an OS command. Here is what I am trying to emulate in my code:

ssh -T myhost < /path/to/some/file

It works fine like this:

def runWorkerCode(filer, filename):
    command = "/usr/bin/ssh -T " + filer + " < /devel/myscript"
    try:
        p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
        out, _ = p.communicate()
    except Exception:
        print "Error: %s" % Exception
        sys.exit(1)
    return out.rstrip().split('\n')

But the following calls to Popen do not work:

        p = subprocess.Popen(["/usr/bin/ssh", "-T", filer, "<", "/devel/myscript"], stdout=subprocess.PIPE, shell=True)
        p = subprocess.Popen(["/usr/bin/ssh -T", filer, "< /devel/myscript"], stdout=subprocess.PIPE, shell=True)

I tried a few other combinations but only method I can get to work is defining the command variable and only providing it to Popen(). I've also tried shell=False.

The first method works but the latter approach seems "cleaner" to me.

Why doesn't Popen allow me to specify the arguments in a list?

回答1:

When you use shell=True on UNIX, you should provide your arguments as a string. When you provide a list, subprocess interprets the first item in the list as your entire command string, and the rest of the items in the list as arguments passed to the shell itself, rather than your command. So in your example above, you're ending up with something like this:

/bin/sh -T filer < /dev/myscript -c "/usr/sbin/ssh"

Definitely not what you meant!

Conversely, when you use shell=False, you can only pass a string if you're running a single command with no arguments. If you do have arguments, have to pass the comamnd as a sequence. You also can't use shell redirection via the < character, because there is no shell involved.

If you want to use shell=False, you can use the stdin keyword argument to pass a file handle to /dev/myscript:

 f = open("/dev/myscript")
 p = subprocess.Popen(["/usr/bin/ssh", "-T", filer], stdout=subprocess.PIPE, stdin=f, shell=False)

The rules for when to pass a string vs. when to pass a sequence are pretty confusing, especially when you bring Windows into the mix as well. I would read the documentation carefully to try to understand it all. Check out both the section on args and the section on shell.