【问题标题】:catch subprocess output spawned by imported module捕获导入模块产生的子进程输出
【发布时间】:2020-02-25 21:25:07
【问题描述】:

注意:我知道我可以使用 stdout=subprocess.PIPE 捕获 subprocess 输出。

在我的情况下,subprocess 将由导入的模块启动,例如youtube-dl 将为ffmpeg 启动一个subprocess,我想捕捉ffmpeg 的输出。

我做了一个虚拟的工作场景来说明我的观点。

我有 2 个文件:m1.py“我的模块”和 m2.py“导入的库”

m2.py

import subprocess
def run():
    print('m2 module message will be caught by echo function too')
    cmd = ['ping', 'google.com']
    subprocess.Popen(cmd)

m1.py

import sys
import m2

# this is a redirection function to echo stdout to a file
def echo(text):
    with open('out.txt', 'a') as f:
        f.write(text)

    sys.stdout.write = original
    sys.stdout.write(text)
    sys.stdout.write = echo

original = sys.stdout.write
sys.stdout.write = echo

print('m1 module message will be caught by echo function')
m2.run()

终端输出:

m1 module message will be caught by echo function
m2 module message will be caught by echo function too

Pinging google.com [172.217.18.46] with 32 bytes of data:
Reply from 172.217.18.46: bytes=32 time=86ms TTL=53
Reply from 172.217.18.46: bytes=32 time=78ms TTL=53

out.txt

m1 module message will be caught by echo function
m2 module message will be caught by echo function too

从上面可以清楚地看出,out.txt 文件从两个模块都收到了 2 打印语句,但无法获得其他输出

这里的问题,我怎样才能得到 m2.py 启动的“ping”命令的输出,“我肯定不能更改 m2.py 的代码,因为它是第 3 方库并且每周更新一次”

【问题讨论】:

    标签: python subprocess stdout


    【解决方案1】:

    注意:这可能只是部分响应,因为它是操作系统低级别。它在 linux 上对我有用,但我不能就 windows 或其他操作系统发表声明

    使用echo() 函数重定向输出的尝试不起作用,因为这只会在您的python 进程中覆盖write。 ping 命令(或由您的第 3 方模块启动的任何命令)是一个不继承您的 python 环境的新进程,尤其不是重写的 write() 函数。

    但是 - 当产生一个新进程时,它确实继承了标准输出文件句柄(我的意思是低级 os 文件句柄,对于 unix 上的标准输出为 1)

    我可以使用以下代码重定向您的 ping 输出。

    import os
    
    ...
    out2 = open('out2.txt', 'w')
    fd = out2.fileno()  # here you get the low level file handle of the new file obj
    os.dup2(fd, 1)      # here you are closing your stdout handle and setting it to out2
    
    ...
    m2.run()     # the spawned ping command is now writing to out2
    

    现在这是重定向到一个文件对象。 我了解您想阅读程序中的输出。

    要实现这一点,您可以在 python 中创建一个管道并改用该文件句柄:

    pipein, pipeout = os.pipe()
    

    注意 pipein、pipeout 已经是低级文件句柄。要将其中之一用作 python 文件对象,您可以使用 os.fdopen(fd) 创建一个

    【讨论】:

    • 它看起来很有希望,刚刚在 Windows 上测试了相同的代码并将所有输出收集在 out2.txt 中,正如你所说,我只需要回显输出即可将其用于 gui,所以我会检查操作系统.pipe() 让你知道结果
    【解决方案2】:

    只要返回subprocess运行的结果并正常赋值即可

    平方米

    import subprocess
    def run():
        print('m2 module message will be caught by echo function too')
        cmd = ['ping', 'google.com']
        return subprocess.Popen(cmd)
    

    m1

    import sys
    import m2
    
    # this is a redirection function to echo stdout to a file
    def echo(text):
        with open('out.txt', 'a') as f:
            f.write(text)
    
        sys.stdout.write = original
        sys.stdout.write(text)
        sys.stdout.write = echo
    
    original = sys.stdout.write
    sys.stdout.write = echo
    
    print('m1 module message will be caught by echo function')
    process_object = m2.run()
    output, err = process_object.communicate()
    print(output, err)
    

    希望对你有帮助

    【讨论】:

    • 不幸的是,我无法更改导入模块 m2.py 中的任何代码,它是第 3 方库,但我出于好奇尝试了您的代码,它抛出了错误 TypeError: 'Popen' object is not iterable
    • 我的错,我忘了加communicate()。这就是给输出带来错误的原因。我已对我的答案进行了更正。如果您确定子进程对象正在从 m2 模块返回,那么这应该可以工作
    • 不,子进程对象没有从 m2 模块返回,谢谢
    猜你喜欢
    • 2013-02-09
    • 1970-01-01
    • 1970-01-01
    • 2016-07-21
    • 1970-01-01
    • 2021-11-15
    • 2018-11-03
    • 2011-02-01
    • 1970-01-01
    相关资源
    最近更新 更多