Частичное чтение из "stdout" на Python с использованием Popen

0 vyazikov [2014-10-06 10:42:00]

Я пытаюсь создать скрипт python, который открывает подпроцесс (скрипт bash) и читает "stdout" в переменную в течение 10 секунд. Через 10 секунд мне нужно передать данные на сервер через запрос POST. Как сделать запрос POST, который я знаю, но как собрать "stdout" в течение 10 секунд?

Я нахожу много примеров, как использовать "Popen", запускать скрипт bash и читать stderr мгновенно без бифференциации, но как собирать выходные данные в течение некоторого времени и выпуска частично?

python bash popen stdout


3 ответа


0 Решение mrad [2014-10-06 12:39:00]

Я думаю, что это решение с двумя потоками с простой ответственностью является чистым и элегантным.

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 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()

0 vyazikov [2014-10-06 14:37:00]

okay позволяет продолжить работу с скриптом 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)

0 user3 [2014-10-06 11:31:00]

Вы можете сделать что-то похожее на то, что ниже:

  1. укажите подпроцесс для вывода на консольный вывод
  2. улавливать выход в переменной, которая является общей для функции публикации и захвата
  3. настроить поток для публикации журналов каждые 10 секунд

импорт потоков, sys, подпроцесс

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()