如何使用os.spawnv发送Python邮件副本?
首先,我想说我知道使用subprocess模块会更好,但我在编辑别人的代码,尽量少改动,所以不想引入新的模块。如果可以的话,我希望能继续使用当前已经导入的模块(os、sys和paths)。
现在的代码在一个叫做postfix-to-mailman.py的文件里,可能有些人会熟悉:
if local in ('postmaster', 'abuse', 'mailer-daemon'):
os.execv("/usr/sbin/sendmail", ("/usr/sbin/sendmail", 'first@place.com'))
sys.exit(0)
这个代码运行得很好(不过我觉得sys.exit(0)可能永远不会被调用,所以可能是多余的)。
我相信这段代码是用来替换当前的进程,调用/usr/sbin/sendmail,并传入参数/usr/sbin/sendmail(也就是它自己)和'someaddress@someplace.com',然后把当前进程的环境,包括在sys.stdin里的邮件内容,传递给子进程。
我想做的基本上是在执行这个之前再发送一份邮件。我不能再用execv,因为那样执行就会停止。所以我试了以下代码:
if local in ('postmaster', 'abuse', 'mailer-daemon'):
os.spawnv(os.P_WAIT, "/usr/sbin/sendmail", ("/usr/sbin/sendmail", 'other@place.com'))
os.execv("/usr/sbin/sendmail", ("/usr/sbin/sendmail", 'first@place.com'))
sys.exit(0)
然而,虽然它把邮件发送到了other@place.com,但却从来没有发送到first@place.com。
这让我很惊讶,因为我以为使用spawn会启动一个子进程,然后在返回时继续当前进程的执行(或者如果使用P_NOWAIT就不等它)。
顺便说一下,我先尝试了os.P_NOWAIT,但我在other@place.com收到的邮件是空的,所以至少使用P_WAIT时邮件内容是完整的。但它还是没有发送到first@place.com,这就成了问题。
如果可以的话,我不想使用os.system,因为我不想进入shell环境(出于安全问题和可能的性能考虑?我承认我有点过于谨慎,但如果能避免os.system,我还是希望能做到)。
我唯一能想到的就是os.spawnv的调用可能以某种方式消耗了sys.stdin的内容,但这也说不通。有什么想法吗?
2 个回答
sys.stdin 就像一个管道,而这种管道是不能回溯的,所以你不能把这个文件样的东西倒回去再读一次。要想真正调用 sendmail(1) 两次,你需要先保存 stdin 的内容,最好是存到一个临时文件里。不过,如果你确定数据的大小是有限的,也可以把它保存在内存中。
但为什么要这么麻烦呢?你真的需要把邮件的副本单独排队发送吗(如果需要的话,为什么呢)?其实可以直接在第一次调用 sendmail(1) 的时候就把想要的收件人加上。这样,额外的收件人不会在邮件的头部显示出来。
if local in ('postmaster', 'abuse', 'mailer-daemon'):
os.execv("/usr/sbin/sendmail", ("/usr/sbin/sendmail",
'first@place.com',
'otheruser@example.com'))
sys.exit(0)
哦,还有,如果 os.execv() 出现问题,sys.exit(0) 这一行就会被执行。这种情况会发生在 /usr/sbin/sendmail 无法执行的时候,比如说这个可执行文件不存在或者根本不能执行。换句话说,这是一种错误情况,你需要处理一下。
虽然这听起来可能有点奇怪,但似乎确实是这样的。
import os
os.spawnv(os.P_WAIT,"/usr/bin/wc", ("/usr/bin/wc",))
os.execv("/usr/bin/wc", ("/usr/bin/wc",))
$ cat j.py | python j.py
4 6 106
0 0 0
在这种情况下,你可以尝试这样做。
import os
import sys
buf = sys.stdin.read()
wc = os.popen("usr/sbin/sendmail other@place.com","w")
wc.write(buf)
wc.close()
wc = os.popen("usr/sbin/sendmail first@place.com","w")
wc.write(buf)
wc.close()
sys.exit(0)