read subprocess stdout line by line

后端 未结 9 2061
一个人的身影
一个人的身影 2020-11-22 02:15

My python script uses subprocess to call a linux utility that is very noisy. I want to store all of the output to a log file and show some of it to the user. I thought the

相关标签:
9条回答
  • 2020-11-22 02:54

    A function that allows iterating over both stdout and stderr concurrently, in realtime, line by line

    In case you need to get the output stream for both stdout and stderr at the same time, you can use the following function.

    The function uses Queues to merge both Popen pipes into a single iterator.

    Here we create the function read_popen_pipes():

    from queue import Queue, Empty
    from concurrent.futures import ThreadPoolExecutor
    
    
    def enqueue_output(file, queue):
        for line in iter(file.readline, ''):
            queue.put(line)
        file.close()
    
    
    def read_popen_pipes(p):
    
        with ThreadPoolExecutor(2) as pool:
            q_stdout, q_stderr = Queue(), Queue()
    
            pool.submit(enqueue_output, p.stdout, q_stdout)
            pool.submit(enqueue_output, p.stderr, q_stderr)
    
            while True:
    
                if p.poll() is not None and q_stdout.empty() and q_stderr.empty():
                    break
    
                out_line = err_line = ''
    
                try:
                    out_line = q_stdout.get_nowait()
                except Empty:
                    pass
                try:
                    err_line = q_stderr.get_nowait()
                except Empty:
                    pass
    
                yield (out_line, err_line)
    

    read_popen_pipes() in use:

    import subprocess as sp
    
    
    with sp.Popen(my_cmd, stdout=sp.PIPE, stderr=sp.PIPE, text=True) as p:
    
        for out_line, err_line in read_popen_pipes(p):
    
            # Do stuff with each line, e.g.:
            print(out_line, end='')
            print(err_line, end='')
    
        return p.poll() # return status-code
    
    0 讨论(0)
  • 2020-11-22 02:58

    Indeed, if you sorted out the iterator then buffering could now be your problem. You could tell the python in the sub-process not to buffer its output.

    proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
    

    becomes

    proc = subprocess.Popen(['python','-u', 'fake_utility.py'],stdout=subprocess.PIPE)
    

    I have needed this when calling python from within python.

    0 讨论(0)
  • 2020-11-22 03:00

    You can also read lines w/o loop. Works in python3.6.

    import os
    import subprocess
    
    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    list_of_byte_strings = process.stdout.readlines()
    
    0 讨论(0)
提交回复
热议问题