【问题标题】:Partial read from "stdout" on Python using Popen [duplicate]使用 Popen 从 Python 上的“stdout”部分读取 [重复]
【发布时间】:2014-10-06 07:42:44
【问题描述】:

我正在尝试构建一个 python 脚本,它打开一个子进程(bash 脚本)并在 10 秒内将“stdout”读入一个变量。 10 秒后,我需要通过 POST 请求将数据传输到服务器。 我知道如何发出 POST 请求,但如何在 10 秒内收集“stdout”?

我找到了很多示例如何使用“Popen”,启动 bash 脚本并立即读取 stderr 而不会产生分歧,但是如何在一段时间内收集输出并部分释放?

【问题讨论】:

  • 你能提供一些你一直在尝试的代码吗?所以我们可以更好地理解,并将其作为答案的基础。

标签: python bash stdout popen


【解决方案1】:

我认为这个具有两个简单职责的线程的解决方案干净优雅。

import os
import subprocess
import threading
import functools

from time import sleep

class OutputMonitor(threading.Thread):

    """ Start the subprocess in separate thread and append it's output to a buffer. """

    def __init__(self, cmd):
        super(OutputMonitor, self).__init__()
        self.daemon = True
        self.cmd = cmd
        self.buffer = ''
        self.buflock = threading.Lock()

    def run(self):

        popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

        while popen.poll() is None:
            data = popen.stdout.read(4)
            if data != "":
                with self.buflock:
                    self.buffer += data

    def get_current_output(self):
        with self.buflock:
            buf = self.buffer
            self.buffer = ""
            return buf

class OutputHandler(threading.Thread):

    """
        Start a thread responsible for tracking subprocess output, and periodically
        check if it has produced new output. If so, call handler to process this data.
    """

    def __init__(self, cmd, interval, filepath):
        super(OutputHandler, self).__init__()
        self.om = OutputMonitor(cmd)
        self.interval = interval

        # Replace it with your handler init...
        self.filepath = filepath
        if os.path.exists(self.filepath):
            os.unlink(self.filepath)

    def run(self):

        self.om.start()
        while self.om.is_alive():
            sleep(self.interval)
            data = self.om.get_current_output()

            self._handle_data_chunk(data)

    def _handle_data_chunk(self, data):

        # Replace it with you handling.
        with open(self.filepath, 'a') as f:
            f.write(data)


if __name__ == '__main__':

    logfile_path = "C:\\log.txt"

    interval = 5
    cmd = ['ping', 'n', '10', '127.0.0.1']

    oh = OutputHandler(cmd, interval, logfile_path)
    oh.start()
    oh.join()

【讨论】:

  • 感谢您的帮助!但似乎在第一次输出后进程挂起......我在 5 秒后只看到第一个输出,之后什么都没有发生......
  • 我放入此示例的 cmd 永远运行,因此它确实挂起。您应该将其替换为 bash 脚本的路径,并将 shell=True 添加到 subprocess.Popen 调用中。此外,timeout 是临时设置的,因此请将其替换为所需的值。我已经更改了代码以满足您的要求。再试一次。
  • 是的,我了解超时,测试 5 秒很好)感谢代码我用简单的cmd = 'ping google.com' 再次尝试它,它再次挂起......我只看到第一个输出和以前一样......我也尝试过使用我的脚本.. 我启动 FFMPEG ffmpeg -i .....-loglevel verbose ....... 并立即看到输出。 !)))
  • 所以完整的任务是从 FFMPEG 中获取标准输出并将其发送到服务器。并且每隔 x 秒做一次
  • 所以你需要每 10 秒更新一次这个缓冲区?起初我明白你只想要输出的前 10 秒,这就是它的工作原理。请澄清。
【解决方案2】:

你可以做类似下面的事情:

  1. 将子进程指向输出到控制台输出
  2. 在发布和捕获函数常用的变量中捕获输出
  3. 设置线程以每 10 秒发布一次日志

导入线程、系统、子进程

out = ""
def postLogs():
    print out
    #do your posting here
    threading.Timer(10.0, postLogs).start() #execute this function every 10 seconds

proc = subprocess.Popen("ping google.com", shell=True,stdout=subprocess.PIPE,     stderr=subprocess.STDOUT)
while proc.poll() is None:
    out = proc.stdout.readline()
    sys.stdout.flush
    if out != "":
        postLogs()

【讨论】:

  • 感谢您的支持.. 但似乎它不起作用(我使用 Linux)我尝试了 subprocess.Popen("ping google.com" 并且我立即看到了输出..
  • 我已经编辑了我的解决方案,我将 C:\ 作为当前工作目录,这是 unix 失败的原因。
  • 试过了,不行(不行......我看到来自 PING 命令的即时消息
【解决方案3】:

好的,让我们继续mrad的脚本 我稍微编辑一下。添加了写入文件功能,它运行完美。与

ping google.com

但它不适用于我需要的命令...我需要启动 ffmpeg。我需要的命令是

ffmpeg -i "my rtsp link" -vcodec copy -loglevel verbose -an -f flv "my RTMP link"

当我将命令放入此代码 1- 时,我会立即看到输出。 2-文件中没有保存任何内容(

import subprocess
import threading
from datetime import datetime
from time import sleep   
from Queue import Queue

class Monitor(threading.Thread):

    def __init__(self, queue, cmd):
        super(Monitor, self).__init__()
        self.queue = queue
        self.cmd = cmd

    def run(self):
        popen = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, shell=True)
        while popen.poll() is None:
            line = popen.stdout.readline()
            self.queue.put(line)       

def foo(cmd, interval):

    q = Queue()
    m = Monitor(q, cmd)
    m.start()

    while m.is_alive():
        sleep(interval)
        current_queue_length = q.qsize()
        chunk = ''
        for i in xrange(current_queue_length):
            chunk += q.get()
    print chunk 
    f=open("/home/pi/raf/log.txt","a")  #trying to write to log 
    f.write(chunk)      
    f.close()


if __name__ == '__main__':
    cmd = 'ping google.com'
    interval = 3
    foo(cmd, interval)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    • 2014-09-13
    • 2012-10-31
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    相关资源
    最近更新 更多