python子进程模块的动态输出

Posted

技术标签:

【中文标题】python子进程模块的动态输出【英文标题】:Dynamic output from python subprocess module 【发布时间】:2019-04-13 05:37:43 【问题描述】:

如何在 python 中使用子进程模块(在外部程序继续运行时)动态实现输出。我想动态获取输出的外部程序是 ngrok , 只要我的程序正在运行,ngrok 就会一直运行,但我需要在进程运行时输出,以便我可以提取新生成的“转发 url”

当我尝试做的时候:

cmd = ['ngrok', 'http', '5000']
output = subprocess.Popen(cmd, stdout=subprocess.PIPE, buffersize=1)

它不断将输出存储在缓冲区中

【问题讨论】:

Printing output in realtime from subprocess 的可能重复项。尽管如此,这只是问题本身,但它不适用于 ngrok 或其他 ncurses 应用程序。所以把这个留给其他最终想知道如何从subprocess 获取输出的人。 【参考方案1】:

我知道这是重复的,但我现在找不到任何相关的线索。我得到的只是output.communicate()

所以这里有一个可能有用的 sn-p:

import subprocess
cmd = ['ngrok', 'http', '5000']
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

while process.poll() is None:
    print(process.stdout.readline())
print(process.stdout.read())
process.stdout.close()

这将通过您的脚本将流程输出的任何内容输出到您的输出中。它通过在输出前查找换行符来实现。

如果不是因为ngrok 使用 ncurses 和/或将输出占用到它自己的用户/线程,就像 SSH 在您执行 ssh user@host 时要求输入密码时一样,这段代码会起作用.

process.poll() 检查进程是否有退出代码(如果它已经死了),如果没有,它会继续循环并打印来自进程的stdout 的任何内容。

还有其他(更好的)方法可以解决这个问题,但这是我可以给你的最低限度,而且不会很快变得复杂。

例如,process.stdout.read() 可以与select.select() 结合使用,以在换行符吓人的情况下实现更好的缓冲输出。因为如果 \n 永远不会出现,上面的示例可能会挂起您的整个应用程序。

在执行此类手动操作之前,您需要注意很多缓冲区陷阱。否则,请改用process.communicate()

编辑:要解决 ngrok 使用的 I/O 的占用/限制,您可以使用 pty.fork() 并通过 os.read 模块读取子标准输出:

#!/usr/bin/python

## Requires: Linux
## Does not require: Pexpect

import pty, os
from os import fork, waitpid, execv, read, write, kill

def pid_exists(pid):
    """Check whether pid exists in the current process table."""
    if pid < 0:
        return False
    try:
        kill(pid, 0)
    except (OSError, e):
        return e.errno == errno.EPERMRM
    else:
        return True

class exec():
    def __init__(self):
        self.run()

    def run(self):
        command = [
                '/usr/bin/ngrok',
                'http',
                '5000'
        ]

        # PID = 0 for child, and the PID of the child for the parent    
        pid, child_fd = pty.fork()

        if not pid: # Child process
            # Replace child process with our SSH process
            execv(command[0], command)

        while True:
            output = read(child_fd, 1024)
            print(output.decode('UTF-8'))
            lower = output.lower()

            # example input (if needed)
            if b'password:' in lower:
                write(child_fd, b'some response\n')
        waitpid(pid, 0)

exec()

这里仍然存在问题,我不太确定这是什么问题或原因。 我猜这个过程正在等待一个信号/以某种方式刷新。 问题是它只打印 ncurses 的第一个“设置数据”,这意味着它会擦除屏幕并设置背景颜色。

但这至少会给你这个过程的输出。替换 print(output.decode('UTF-8')) 会告诉你输出是什么。

【讨论】:

在程序到达这一行后,它看起来程序卡在这行代码 """process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)"""开始将输出存储在缓冲区中并保持沉默.. 在 ngrok 运行时我想从 ngrok 中提取的唯一内容是 ngrok 提供的远程 url @arslanmughal 你说得对,但它并没有停留在Popen() 行。它卡在print(process.stdout.readline()) 上。这是一个巨大的不同,因为它只是告诉我们Popen() 无法从进程中获取输出。我也试过stdout.read(1),它什么也没返回。所以我添加了另一种获取输出的方法。

以上是关于python子进程模块的动态输出的主要内容,如果未能解决你的问题,请参考以下文章

python子进程模块subprocess详解与应用实例 之二

在 Python 子进程模块中过滤掉需要终端的命令

Python:产生具有几个要求的子进程

如何在python子进程模块中执行用户输入(如日期)作为命令[重复]

Python2/3 中执行外部命令(Linux)和程序(exe) -- 子进程模块 subprocess

如何在 node.js 子进程模块中将消息和标准输出从子进程传递给父进程?