subprocess.Popen执行ssh命令时遇到问题

1 投票
1 回答
2934 浏览
提问于 2025-04-18 15:13

我正在使用subprocess.Popen来执行操作系统的命令。这里是我想在代码中模拟的内容:

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

这样做是可以正常工作的:

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')

但是下面这些调用Popen的方式就不行:

        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)

我尝试了几种其他组合,但唯一能正常工作的方式是先定义一个命令变量,然后把它传给Popen()。我也试过shell=False

第一种方法有效,但我觉得后面那种方式看起来更“干净”。

为什么Popen不允许我用列表来指定参数呢?

1 个回答

2

在UNIX系统中,当你使用 shell=True 时,应该把参数作为一个字符串提供。如果你提供的是一个列表,subprocess 会把列表中的第一个项当作整个命令字符串,而把列表中其余的项当作传给shell的参数,而不是你的命令。所以在你上面的例子中,结果可能会变成这样:

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

这肯定不是你想要的结果!

相反,当你使用 shell=False 时,如果你只运行一个没有参数的单一命令,才可以传一个字符串。如果有参数,就必须把命令作为一个序列传递。而且你也不能通过 < 字符使用shell重定向,因为没有使用shell。

如果你想使用 shell=False,可以通过 stdin 关键字参数把文件句柄传给 /dev/myscript

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

关于何时传字符串和何时传序列的规则其实挺复杂的,尤其是当你还考虑到Windows的时候。我建议你仔细阅读 文档,试着理解这些内容。可以查看 args 部分和 shell 部分。

撰写回答