【问题标题】:subprocess.Popen: how to terminate subprocess and receive its output?subprocess.Popen:如何终止子进程并接收其输出?
【发布时间】:2012-09-14 12:14:01
【问题描述】:

我想从 python 运行一个 shell 命令并使用 subprocess.Popen 接收它的输出。问题是,当我关闭进程并发送 Ctrl-C 时,我没有得到任何输出。我究竟做错了什么?代码:

>>> import subprocess
>>> sub = subprocess.Popen(["xinput", "test", "8"], stdout=subprocess.PIPE) #receive mouse events
>>> output = sub.communicate()[0].read()
^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/subprocess.py", line 693, in communicate
    stdout = self.stdout.read()
KeyboardInterrupt
>>> output
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'output' is not defined

受 Jett 这篇文章的启发:

Reading stdout from xinput test in python

【问题讨论】:

  • 发生的情况是你执行了一个 python 语句,然后通过 ctrl+C 从命令行终止它,这意味着输出没有被分配,因为异常得到引发,因此未分配输出并且您得到 NameError。通过 kill 或其他方式关闭进程而不终止 python 语句
  • @pythonm -- 或通过任何异常终止它。
  • @Bob,使用 xinput 要记住的一件事是,如果您开始从测试中读取数据,它将被缓冲,因此您只会获得其中的一部分,empty.sourceforge.net 是一种可能的解决方案,因为它是伪造的TTY 似乎需要实时输出。
  • @jett 谢谢你,但我不确定,我明白你的意思,当谈到缓冲时。谁缓冲数据,为什么我只会得到数据块?为什么我需要伪造 TTY?你建议使用pexpect 模块? – 鲍勃
  • xinput test 似乎只有在连接到 TTY 时才实时输出数据,否则它将输出存储到缓冲区中,并在缓冲区填满后输出。这是进一步信息的回复。 stackoverflow.com/a/12426921/778858

标签: python subprocess stdout


【解决方案1】:

这里的问题是KeyboardInterrupt 在调用communicate 期间被发送。结果,communicate 永远不会返回,因此它的输出永远不会存储在变量output 中,当您尝试使用它时会得到NameError。一种解决方法如下:

 import subprocess
 sub = subprocess.Popen(["xinput", "test", "8"], stdout=subprocess.PIPE)
 lines = [] #Need someplace to store the data as it comes
 try:
    for line in sub.stdout: #read one line from standard out, store it in lines
        lines.append(line)
 except KeyboardInterrupt:  #keyboardInterrupt happened.  Stop process
    sub.terminate()
 finally:                   #Join our lines into a single buffer (like `communicate`)
    output = ''.join(lines)
    del lines               #clean `lines` out of our namespace (just because). 

【讨论】:

  • 谢谢,我知道了。不会再在一行中混合复杂的操作。
【解决方案2】:

@pythonm 已经解释了NameError

此外,您使用的Popen.communicate() 的输出在概念上是错误的。它返回一个 2 元组字符串:(stdout, stderr)。它不返回两个类似文件的对象。这就是为什么如果 communicate() 返回,您的 sub.communicate()[0].read() 会失败。

在子进程返回之前,communicate() 会聚合其所有的 stdout 和 stderr(考虑到您向构造函数提供了 stdout=subprocess.PIPEstderr=subprocess.PIPE)。只有在子进程终止后,您才能访问communicate() 在子进程运行时收集的内容。

如果您想实时监控子进程的输出,那么communicate() 是错误的方法。运行子进程,监视它(例如在循环中)并与它的Popen.stdoutPopen.stderr 属性(它们是类似文件的对象)进行交互。 @mgilson 的回答向您展示了一种方法:)

【讨论】:

  • 谢谢,扬-菲利普。 “子进程”作者命名“通信”stdout/stderr/stdin 的参数和输出是非常具有欺骗性的想法,尽管它们实际上是 str。像我这样不耐烦的读者会被抓住。 :)
猜你喜欢
  • 1970-01-01
  • 2019-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-18
  • 1970-01-01
相关资源
最近更新 更多