Getting realtime output using subprocess

前端 未结 18 2596
执笔经年
执笔经年 2020-11-22 10:12

I am trying to write a wrapper script for a command line program (svnadmin verify) that will display a nice progress indicator for the operation. This requires me to be abl

相关标签:
18条回答
  • 2020-11-22 10:51

    This is the basic skeleton that I always use for this. It makes it easy to implement timeouts and is able to deal with inevitable hanging processes.

    import subprocess
    import threading
    import Queue
    
    def t_read_stdout(process, queue):
        """Read from stdout"""
    
        for output in iter(process.stdout.readline, b''):
            queue.put(output)
    
        return
    
    process = subprocess.Popen(['dir'],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT,
                               bufsize=1,
                               cwd='C:\\',
                               shell=True)
    
    queue = Queue.Queue()
    t_stdout = threading.Thread(target=t_read_stdout, args=(process, queue))
    t_stdout.daemon = True
    t_stdout.start()
    
    while process.poll() is None or not queue.empty():
        try:
            output = queue.get(timeout=.5)
    
        except Queue.Empty:
            continue
    
        if not output:
            continue
    
        print(output),
    
    t_stdout.join()
    
    0 讨论(0)
  • 2020-11-22 10:53

    Depending on the use case, you might also want to disable the buffering in the subprocess itself.

    If the subprocess will be a Python process, you could do this before the call:

    os.environ["PYTHONUNBUFFERED"] = "1"
    

    Or alternatively pass this in the env argument to Popen.

    Otherwise, if you are on Linux/Unix, you can use the stdbuf tool. E.g. like:

    cmd = ["stdbuf", "-oL"] + cmd
    

    See also here about stdbuf or other options.

    (See also here for the same answer.)

    0 讨论(0)
  • 2020-11-22 10:53

    Using pexpect with non-blocking readlines will resolve this problem. It stems from the fact that pipes are buffered, and so your app's output is getting buffered by the pipe, therefore you can't get to that output until the buffer fills or the process dies.

    0 讨论(0)
  • 2020-11-22 10:54

    You can try this:

    import subprocess
    import sys
    
    process = subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    
    while True:
        out = process.stdout.read(1)
        if out == '' and process.poll() != None:
            break
        if out != '':
            sys.stdout.write(out)
            sys.stdout.flush()
    

    If you use readline instead of read, there will be some cases where the input message is not printed. Try it with a command the requires an inline input and see for yourself.

    0 讨论(0)
  • 2020-11-22 10:55

    In Python 3.x the process might hang because the output is a byte array instead of a string. Make sure you decode it into a string.

    Starting from Python 3.6 you can do it using the parameter encoding in Popen Constructor. The complete example:

    process = subprocess.Popen(
        'my_command',
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        shell=True,
        encoding='utf-8',
        errors='replace'
    )
    
    while True:
        realtime_output = process.stdout.readline()
    
        if realtime_output == '' and process.poll() is not None:
            break
    
        if realtime_output:
            print(realtime_output.strip(), flush=True)
    

    Note that this code redirects stderr to stdout and handles output errors.

    0 讨论(0)
  • 2020-11-22 10:56

    You can direct the subprocess output to the streams directly. Simplified example:

    subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)
    
    0 讨论(0)
提交回复
热议问题