Python subprocess: 为什么参数列表无法像完整的shell字符串一样工作?

4 投票
4 回答
3611 浏览
提问于 2025-04-16 08:12

非常感谢大家的帮助。我刚开始学习Python,但对脚本编写并不陌生。我想运行一个简单的自动发邮件程序,但我们系统上的邮件模块似乎安装得不太对(我没有Python示例中提到的75%的功能,只能用到“message_from_string”和“message_from_file”),而smtplib对我来说又太复杂了。

实际上,用简单的bash命令来说,我只需要:

/bin/email -s "blah" "recipients" < file.with.body.info.txt

或者,

echo "my body details" | /bin/email -s "blah" "recipients"

这样我就可以避免为了发送消息而写入文件。

我尝试使用subprocess,不管是call还是Popen,最终能让它工作的唯一方法是使用:

subprocess.call('/bin/mail -s "blah" "recipients" < file.with.body.info.txt', shell=True)

我对这种方法有几点特别不喜欢:

(1) 我无法将内容分成列表或元组,这样我就失去了使用subprocess的安全性。如果我尝试:

subprocess.call(['/bin/mail', '-s', subjVariable, recipVariable, '<', 'file.with.body.info.txt'], shell=True)

它就会失败。同样,如果我尝试使用管道符号'|'而不是从文件读取,它也会失败。如果我用'-cmd'而不是管道,它也会失败。通常的“失败”是它会把'<'和'file.with.body.info.txt'当作其他接收者来看待。换句话说,无论我是否设置“shell = True”,subprocess都无法正确理解调用中的特殊字符。'<'没有被识别为文件输入,除非我把所有内容放在一个大的调用中。

我理想中想要做到的,因为这样似乎更安全、更灵活,是这样的:

subprocess.call(['/bin/echo', varWithBody, '|', '/bin/mail', '-s', subjVariable, recipVariable,])

但看起来subprocess根本不理解管道,我无法在Python中将内容连接起来。

有什么建议吗?任何帮助都欢迎,除了解释如何使用'email'或'smtplib'模块的尝试。无论这个特定的应用如何,我真的想更好地学习如何使用subprocess,这样我就可以将不同的程序连接起来。我了解到Python在这方面应该表现得不错。

谢谢!迈克

4 个回答

4

<、| 等符号是命令行的特性,而不是操作系统的特性。所以像 subprocess 这样的东西并不知道这些符号是什么——它内部只是把参数列表传递给相应的操作系统函数。使用 subprocess 进行输入/输出重定向的方法是使用 stdinstdoutstrerr 这些参数。你可以传入一个文件对象(这个对象必须包含一个文件描述符,但通常打开的文件都会有这个),或者直接传入一个文件描述符。还可以使用管道对象。

手册里有一个关于 替换管道 的例子,只需把管道替换成文件对象,你就可以搞定了。

5

|< 不是参数,它们是用来在命令行中进行重定向的符号。要在你的代码中替换 |,可以参考 这些说明

要替换 <,可以使用:

 subprocess.Popen(["command", "args"], stdin=open("file.txt", 'r'))

例如:

subprocess.Popen(["cat"], stdin=open("file.txt", 'r')) 的效果和 cat < file.txt 是一样的。

5

Python的官方文档似乎对这种情况有解释。

我可能会这样做:

from subprocess import *
readBody = Popen(["/bin/echo", varWithBody], stdout=PIPE)
mail = Popen(["/bin/mail", "-s", subjVariable, recipVariable], stdin=readBody.stdout, stdout=PIPE)
output = mail.communicate()[0]

撰写回答