【问题标题】:How to use os.spawnv to send email copy using Python?如何使用 os.spawnv 使用 Python 发送电子邮件副本?
【发布时间】:2011-03-17 05:37:15
【问题描述】:

首先让我说,我知道使用 subprocess 模块会更好,但我正在编辑其他人的代码,并且我正在尝试进行尽可能少的更改,其中包括避免导入任何新模块。因此,如果可能的话,我想坚持使用当前导入的模块(操作系统、系统和路径)。

代码目前(在一个名为 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(对于 argv[0] 即本身)和“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 的内容,但这也没有任何意义。想法?

【问题讨论】:

    标签: python process postfix-mta mailman


    【解决方案1】:

    虽然可能没有意义,但似乎确实如此

    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)
    

    【讨论】:

    • 嗯,这可能是其中的一部分,但不幸的是,在 spawnv 和 execv 行之间插入 sys.stdin = sys.__stdin__ 似乎不起作用,即使docs.python.org/library/sys.html#sys.stdin 说 sys.__stdin__ 应该在程序开始时包含 sys.stdin 的原始值。布莱赫。 (但感谢您对其进行测试并提供解决方案的线索!)
    • 我认为 os.popen 像 os.system 一样调用 shell。有什么方法可以做你所建议的事情而不必去外壳?我尝试使用 popen2, 3, 4 来执行此操作,但是虽然它们接受参数序列并绕过 shell,但它们不会返回可以写入的文件描述符(显然)。谢谢!
    • 顺便说一句,我觉得这里的主要问题是迭代 sys.stdin 是一个一次性的过程;似乎没有办法回到 sys.stdin 的开头进行第二次调用:-/
    • From docs.python.org/library/os.html "另外,对于这些变体中的每一个,在 Unix 上,cmd 可能是一个序列,在这种情况下,参数将直接传递给程序而无需 shell 干预(如 os.spawnv ())。如果 cmd 是一个字符串,它将被传递给 shell(与 os.system() 一样)。"因此,如果您更改为 (wc_in, wc_out) = os.popen2(["usr/sbin/sendmail","first@place.com"],"w") 应该在没有 shell 干预的情况下通过它。但从 python 2.6 开始,popen2 已被弃用。
    • 我试过这个并得到 /usr/bin/wc: write error: Broken pipe。具体来说,我试过: import os, sys buf = sys.stdin.read() (wc_in, wc_out) = os.popen2(["/usr/bin/wc", "-l"], 'w') wc_in.write (buf) wc_in.close()
    【解决方案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 无法执行,就会发生这种情况,例如如果可执行文件不存在或实际上不是可执行文件。换句话说,这是您应该注意的错误情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-06
      • 2020-11-03
      • 2019-10-18
      • 2012-06-30
      • 2023-02-22
      • 2019-06-26
      相关资源
      最近更新 更多