1

i'm trying to build a python script, which opens a subprocess (bash script) and reads "stdout" into a variable during 10 seconds. After 10 sec i need to transfer data to server via POST request. How to make POST request i know, but how to collect "stdout" during 10 seconds?

i find a lot of examples how to use "Popen", launch bash script and read stderr instantly without biffering, but how to collect output during some time and release is partially?

vyazikov
  • 35
  • 1
  • 8
  • Could you provide some code of what you have been trying? So we can understand better, and use it as a basis for an answer. – jdehesa Oct 06 '14 at 08:09

3 Answers3

0

You could do something similar to what is below:

  1. point the subprocess to output to console output
  2. catch the output in a variable which is common to posting and capturing function
  3. setup the thread to post the logs every 10 seconds

import threading, sys, subprocess

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()
venpa
  • 4,268
  • 21
  • 23
  • Thank you for support.. But seems it not working( I use Linux )i tried `subprocess.Popen("ping google.com"` and i see output instantly.. – vyazikov Oct 06 '14 at 09:33
  • i have edited my solution, i had C:\ as current working directory which was the reason for failure in unix. – venpa Oct 06 '14 at 10:46
  • tried, nope( not works...... i see instant messages from PING command – vyazikov Oct 06 '14 at 11:40
0

I think this solution with two threads with simple responsibilities is clean and elegant.

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()
mrad
  • 46
  • 5
  • thank you for help! but seems that after first output the process hangs.... i see only first output after 5 sec and after that nothing happens anymore..... – vyazikov Oct 06 '14 at 09:52
  • `cmd` which I put into this example runs forever so it indeed hangs. You should replace it with path to your bash script, and add `shell=True` to the `subprocess.Popen` call. Also, the `timeout` is set ad hoc, so replace it with desired value. I've changed the code to meet your requirements. Try it again. – mrad Oct 06 '14 at 09:56
  • yeap i understand about timeout, for testing 5 sec was good) Thanks for code i tried it again with simple `cmd = 'ping google.com'` and it hangs again... i see only first output as before... Also i did try with my script.. i launch FFMPEG `ffmpeg -i .....-loglevel verbose .......` and i see output instantly. !))) – vyazikov Oct 06 '14 at 10:05
  • so the full task is to grab stdout from FFMPEG and send it to server. and to do it partly every x seconds – vyazikov Oct 06 '14 at 10:07
  • So you need to update this buffer every 10 seconds? At first I understood you wanted only the first 10 seconds of the output, and that's how it works. Please clarify. – mrad Oct 06 '14 at 10:08
  • yes i need to update it every 10 seconds! sorry my English skills are not the best!)) – vyazikov Oct 06 '14 at 10:10
  • I've edited my solution. Does it work as you expect right now? – mrad Oct 06 '14 at 10:41
  • please look at my latest answer – vyazikov Oct 06 '14 at 11:37
  • Since you are not really using shell, you should pass arguments to Popen using list, and removing `shell=True`. So, first of all, set `shell=False`, or remove it. Secondly, prepare your input in this way: `cmd = ['ffmpeg', '-i', 'my rtsp link', '-vcodec', 'copy', '-loglevel', 'verbose', '-an', '-f', 'flv', 'my RTMP link']` – mrad Oct 06 '14 at 11:47
  • thank you for information! i did all as you say and the result is the same((( i can give you SSH acces to this PC (its Raspberry) just ping me vvyazikov (at) gmail.com if it interesting to you..) – vyazikov Oct 06 '14 at 12:00
0

okay lets continue with mrad's script i edit it just a little. Added writing to file function and it works perfect. with

ping google.com

BUT it do not work with command which i need... i need to launch ffmpeg. Command which i need is

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

when i put my command inside this code 1- i see output instantly. 2- nothing saved in the file (

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)
vyazikov
  • 35
  • 1
  • 8