Python subprocess readlines() hangs

后端 未结 4 1837
独厮守ぢ
独厮守ぢ 2020-11-22 01:43

The task I try to accomplish is to stream a ruby file and print out the output. (NOTE: I don\'t want to print out everything at once)

4条回答
  •  梦毁少年i
    2020-11-22 02:05

    I assume you use pty due to reasons outlined in Q: Why not just use a pipe (popen())? (all other answers so far ignore your "NOTE: I don't want to print out everything at once").

    pty is Linux only as said in the docs:

    Because pseudo-terminal handling is highly platform dependent, there is code to do it only for Linux. (The Linux code is supposed to work on other platforms, but hasn’t been tested yet.)

    It is unclear how well it works on other OSes.

    You could try pexpect:

    import sys
    import pexpect
    
    pexpect.run("ruby ruby_sleep.rb", logfile=sys.stdout)
    

    Or stdbuf to enable line-buffering in non-interactive mode:

    from subprocess import Popen, PIPE, STDOUT
    
    proc = Popen(['stdbuf', '-oL', 'ruby', 'ruby_sleep.rb'],
                 bufsize=1, stdout=PIPE, stderr=STDOUT, close_fds=True)
    for line in iter(proc.stdout.readline, b''):
        print line,
    proc.stdout.close()
    proc.wait()
    

    Or using pty from stdlib based on @Antti Haapala's answer:

    #!/usr/bin/env python
    import errno
    import os
    import pty
    from subprocess import Popen, STDOUT
    
    master_fd, slave_fd = pty.openpty()  # provide tty to enable
                                         # line-buffering on ruby's side
    proc = Popen(['ruby', 'ruby_sleep.rb'],
                 stdin=slave_fd, stdout=slave_fd, stderr=STDOUT, close_fds=True)
    os.close(slave_fd)
    try:
        while 1:
            try:
                data = os.read(master_fd, 512)
            except OSError as e:
                if e.errno != errno.EIO:
                    raise
                break # EIO means EOF on some systems
            else:
                if not data: # EOF
                    break
                print('got ' + repr(data))
    finally:
        os.close(master_fd)
        if proc.poll() is None:
            proc.kill()
        proc.wait()
    print("This is reached!")
    

    All three code examples print 'hello' immediately (as soon as the first EOL is seen).


    leave the old more complicated code example here because it may be referenced and discussed in other posts on SO

    Or using pty based on @Antti Haapala's answer:

    import os
    import pty
    import select
    from subprocess import Popen, STDOUT
    
    master_fd, slave_fd = pty.openpty()  # provide tty to enable
                                         # line-buffering on ruby's side
    proc = Popen(['ruby', 'ruby_sleep.rb'],
                 stdout=slave_fd, stderr=STDOUT, close_fds=True)
    timeout = .04 # seconds
    while 1:
        ready, _, _ = select.select([master_fd], [], [], timeout)
        if ready:
            data = os.read(master_fd, 512)
            if not data:
                break
            print("got " + repr(data))
        elif proc.poll() is not None: # select timeout
            assert not select.select([master_fd], [], [], 0)[0] # detect race condition
            break # proc exited
    os.close(slave_fd) # can't do it sooner: it leads to errno.EIO error
    os.close(master_fd)
    proc.wait()
    
    print("This is reached!")
    

提交回复
热议问题