live output from subprocess command

后端 未结 16 1477
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-22 08:16

I\'m using a python script as a driver for a hydrodynamics code. When it comes time to run the simulation, I use subprocess.Popen to run the code, collect the

16条回答
  •  说谎
    说谎 (楼主)
    2020-11-22 09:11

    Solution 1: Log stdout AND stderr concurrently in realtime

    A simple solution which logs both stdout AND stderr concurrently, line-by-line in realtime into a log file.

    import subprocess as sp
    from concurrent.futures import ThreadPoolExecutor
    
    
    def log_popen_pipe(p, stdfile):
    
        with open("mylog.txt", "w") as f:
    
            while p.poll() is None:
                f.write(stdfile.readline())
                f.flush()
    
            # Write the rest from the buffer
            f.write(stdfile.read())
    
    
    with sp.Popen(["ls"], stdout=sp.PIPE, stderr=sp.PIPE, text=True) as p:
    
        with ThreadPoolExecutor(2) as pool:
            r1 = pool.submit(log_popen_pipe, p, p.stdout)
            r2 = pool.submit(log_popen_pipe, p, p.stderr)
            r1.result()
            r2.result()
    

    Solution 2: A function read_popen_pipes() that allows you to iterate over both pipes (stdout/stderr), concurrently in realtime

    import subprocess as sp
    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()
                    err_line = q_stderr.get_nowait()
                except Empty:
                    pass
    
                yield (out_line, err_line)
    
    # The function in use:
    
    with sp.Popen(["ls"], stdout=sp.PIPE, stderr=sp.PIPE, text=True) as p:
    
        for out_line, err_line in read_popen_pipes(p):
            print(out_line, end='')
            print(err_line, end='')
    
        p.poll()
    
    

提交回复
热议问题