Python subprocess: 为什么参数列表无法像完整的shell字符串一样工作?
非常感谢大家的帮助。我刚开始学习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 个回答
<、| 等符号是命令行的特性,而不是操作系统的特性。所以像 subprocess 这样的东西并不知道这些符号是什么——它内部只是把参数列表传递给相应的操作系统函数。使用 subprocess 进行输入/输出重定向的方法是使用 stdin
、stdout
和 strerr
这些参数。你可以传入一个文件对象(这个对象必须包含一个文件描述符,但通常打开的文件都会有这个),或者直接传入一个文件描述符。还可以使用管道对象。
手册里有一个关于 替换管道 的例子,只需把管道替换成文件对象,你就可以搞定了。
|
和 <
不是参数,它们是用来在命令行中进行重定向的符号。要在你的代码中替换 |
,可以参考 这些说明。
要替换 <
,可以使用:
subprocess.Popen(["command", "args"], stdin=open("file.txt", 'r'))
例如:
subprocess.Popen(["cat"], stdin=open("file.txt", 'r'))
的效果和 cat < file.txt
是一样的。
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]