【问题标题】:How to read output from subprocess Popen correctly?如何正确读取子进程 Popen 的输出?
【发布时间】:2015-04-09 19:38:28
【问题描述】:

我正在尝试将 stderr 重定向到 stdout 并读取命令的输出。看起来代码可以工作,但是当它到达输出的末尾时,它会抛出一个异常

代码:

with Popen(["bash", "-c", "for ((i=0;i<10;i++)); do echo $i; sleep .5; done"],
           stderr=subprocess.STDOUT) as proc:
    out, err = proc.communicate()
    for line in out:
        if line:
            print(line)

输出:

0
1
2
3
4
5
6
7
8
9
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-3c150d6a4092> in <module>()
      2            stderr=subprocess.STDOUT) as proc:
      3         out, err = proc.communicate()
----> 4         for line in out:
      5                 if line:
      6                         print(line)

TypeError: 'NoneType' object is not iterable

【问题讨论】:

  • stderr=subprocess.PIPE

标签: python python-3.x subprocess


【解决方案1】:

我明白了,您可以使用另一种方法实现您想要的,但我想解释一下您的第一个版本有什么好处。

问题是您在proc 中没有任何用于输出的PIPE,因此输出不会“遍历”您的脚本,而是直接进入屏幕,这就是您看到输出的原因:

0
1
2
3
4
5
6
7
8
9

然后在一行中:

out, err = proc.communicate() # out is None, since you don't 
                              # pass the argument "stdout=subprocess.PIPE"
                              # when creating proc.

当您尝试迭代 out 时:

for line in out:  # TypeError: 'NoneType' object is not iterable
     ...          # We are talking about out variable here, which is None.
     print(line)  # This line never get hit.

解决方案。

这里有一个将stdout 重定向到stderr 并使用communicate 的小示例:

import subprocess
import sys

proc = subprocess.Popen(['ls'], stderr=sys.stdout, shell=True) # Not the use of sys.stdout
out, err = proc.communicate()
print(err.decode("utf-8"))

【讨论】:

    【解决方案2】:

    TypeError: 'NoneType' 对象不可迭代

    你应该添加stdout=PIPE,否则.communicate()总是out返回None

    顺便说一句,如果您的代码使用stdout=PIPE, stderr=STDOUT,那么err 总是 Noneout 包含合并的 stdout/stderr 作为单个字节串,即 for byte in out 产生一个字节一次,不是行。

    如果您设置stdout=PIPE, stderr=PIPE 并调用.communicate(),您可以单独获取stdout/stderr。

    【讨论】:

      【解决方案3】:

      哦,看来我需要设置stdout=PIPE,不需要communicate()

      with Popen(["bash", "-c", "for ((i=0;i<10;i++)); do echo $i; sleep .5; done"],
                 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc:
          for line in proc.stdout:
              print(line.decode().rstrip())
      

      使用 J.F. Sebastian 的 universal_newlines 提示:

      with Popen(["bash", "-c", "for ((i=0;i<10;i++)); do echo $i; sleep .5; done"],
                 stdout=subprocess.PIPE, 
                 stderr=subprocess.STDOUT, 
                 universal_newlines=True) as proc:
          for line in proc.stdout:
              print(line, end='')
      

      【讨论】:

      • 你可以放弃if line条件; line 在这里永远不会为空(不会删除换行符)。您可以使用universal_newlines=True 启用文本模式(以避免手动调用line.decode())。在这种情况下,您可以只使用 print(line, end='')
      猜你喜欢
      • 2011-03-11
      • 2011-10-02
      • 1970-01-01
      • 2012-06-07
      • 2014-08-28
      • 2018-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多