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子进程模块中执行用户输入(如日期)作为命令[重复]