【问题标题】:how to call stdout.write from subprocess.Popen?如何从 subprocess.Popen 调用 stdout.write?
【发布时间】:2014-02-26 13:44:17
【问题描述】:

我正在尝试通过标准输出将使用 subprocess.Popen() 执行的命令记录到 UDP 套接字。我在网上看到过尝试,但至少在我的情况下没有真正成功。

有些人试图继承一个套接字并将其传递给 Popen 的“stdout”参数,以覆盖默认的 .write 方法以通过套接字发送,例如,参见http://www.dreamincode.net/forums/topic/209734-using-a-socket-as-stdinstdout/

我还尝试继承“文件”类并用我的实现覆盖 write 方法,但 Popen 似乎根本没有调用任何 write 方法。

确实,这种子类化的想法行不通,原因很简单,即 Popen(args,stdout=mysocket) 不调用任何 mysocket.write(或任何 .write)方法。为了测试这一点,我在调试模式下调用了 Popen,并尝试从它的 init 单步执行到最后,但没有找到任何 .write 或类似的东西。我正在使用 python 2.7

当然我知道我可以将标准输出发送到 PIPE 然后读取它,但这需要在我的情况下创建(多个)工作线程来读取 PIPE 并发送,如果可能的话我想避免这个解决方案如果可以用更简单的方式获得更简单的方法。

我的文件子类

class Foo(file):
    dbgIP = ""
    dbgPORT = 0
    sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

    def __init__(self, dbgIP, dbgPORT):
        file.__init__(self,"empty",'w')
        self.sck.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
        self.dbgIP = dbgIP
        self.dbgPORT = dbgPORT

    def write(self, text):
        return self.sck.sendto(text, (self.dbgIP, self.dbgPORT))

一个示例调用

foofile = Foo(MCAST_GROUP,MCAST_PORT)
subprocess.Popen("jackd -R -P62 -dalsa -dhw:0 -p256 -n2 -i2 -o2 -s -S -r22050",shell=True, stdout=foofile, stderr=subprocess.STDOUT)

调用foofile.write("asd") 工作正常

【问题讨论】:

  • 您是否尝试在Foo.write 中插入打印语句只是为了确保它没有被调用?
  • 是的,仔细检查,类被正确实例化(在构造函数末尾用打印检查),套接字工作(如果调用foofile.write("asd"),但写入从未被 Popen 调用(因此没有您建议的控制台打印出现)
  • Popen 实现是否及时改变?我正在运行 python 2.7.5+
  • 我正在阅读Popen 的源代码,但无法了解它如何处理对标准输出的写入。据我所知,它应该有效。
  • 如果没有给出任何stdout,该命令是否会向控制台输出一些内容?

标签: python udp popen


【解决方案1】:

Popen() 在文件描述符(处理程序)级别工作。这意味着子类化和覆盖.write() 方法将不起作用。在某些系统上real socket (valid .fileno()) can be passed to stdout as is

from subprocess import call, STDOUT

rc = call("./generate-output", stdout=sck, stderr=STDOUT)

在其他系统上,您可以手动完成:

import shutil
from subprocess import Popen, PIPE, STDOUT

p = Popen("./generate-output", stdout=PIPE, stderr=STDOUT, bufsize=-1)
shutil.copyfileobj(p.stdout, foofile)
p.stdout.close()
rc = p.wait()

在更一般的情况下,您可以使用类似于 teed_call() 的内容。


1) 对于将套接字用作标准输出,如何确定哪些系统可以工作?这取决于Unix风味/Win,还是python版本?

我已经在 Linux 上试过了。我希望它可以在 POSIX 系统上运行。

2) 如果我想对套接字进行子类化,我应该修改哪些方法,例如向来自标准输出的字符串添加一个字符串?

在这种情况下,您不应该继承套接字。 .fileno()subprocess 调用的唯一方法。 “在文件描述符级别工作”意味着一旦你给出了文件描述符并且发生了重定向,剩下的就是内核业务。

3) 我假设带有 copyfileobj 的代码将启动从 stdout 到 foofile 的复制,直到进程处于活动状态。但是,关闭 stdout 不会停止进程和 foofile 之间的通信吗? ——

copyfileobj() 复制到 p.stdout 中的 EOF。 It is pure Python:

def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)

p.stdout.close() 被调用以避免泄漏文件描述符(用于清理)。

有一个缓冲区,因此子进程终止后仍可能进行复制。当然,子进程可能会在它还活着的时候关闭它的标准输出。

【讨论】:

  • 谢谢。所有这些选项听起来都不错。我有几个问题: 1. 对于将套接字用作标准输出,如何确定哪些系统可以工作?这取决于 Unix 风格/Win,还是 python 版本? 2.如果我想子类化套接字,我应该修改哪些方法,例如向来自标准输出的字符串添加一个字符串? 3. 我假设带有 copyfileobj 的代码将启动从 stdout 到 foofile 的复制,直到进程处于活动状态。但是,关闭 stdout 不会停止进程和 foofile 之间的通信吗?
  • @KeinReverbLeonardoGabrielli:我已经回答了您评论中的问题
  • 谢谢,附带说明一下,您提供的第一种方法仅适用于 TCP 等面向连接的套接字,因为在将套接字提供给 Popen 时必须知道目的地。
猜你喜欢
  • 2011-10-25
  • 1970-01-01
  • 1970-01-01
  • 2016-03-28
  • 2012-07-12
  • 2016-09-29
  • 1970-01-01
  • 2022-08-03
  • 2011-07-07
相关资源
最近更新 更多