如何在 Linux 中使用 Python 从进程中读取非阻塞输出

103 阅读1分钟

当使用 Python 的 subprocess 模块从进程中读取输出时,代码没有产生任何输出。

  • 在 IDLE 中运行以下代码时,始终打印“no output yet”。

image.png

import sys
from subprocess import PIPE, Popen
from threading  import Thread
http://www.jshk.com.cn/mb/reg.asp?kefu=xiaoding;//爬虫IP免费获取;

try:
    from Queue import Queue, Empty
except ImportError:
    from queue import Queue, Empty  # python 3.x

ON_POSIX = 'posix' in sys.builtin_module_names

def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        queue.put(line)
    out.close()

p = Popen(['youtube-dl', '-l', '-c', 'https://www.youtube.com/watch?v=utV1sdjr4PY'],   stdout=PIPE, bufsize=1, close_fds=ON_POSIX)

q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True # thread dies with the program
t.start()

# ... do other things here

# read line without blocking
while True:
    try:  line = q.get_nowait() # or q.get(timeout=.1)
    except Empty:
        pass
        #print('no output yet')
    else: # got line
        print line

2、解决方案

解决方案一:

  • 使用 pty 模块来创建伪终端,并将进程的标准输出和标准错误重定向到伪终端的从设备。
  • 使用 os.fdopen 打开伪终端的主设备,然后使用 readline 方法逐行读取输出。
import sys, os
from subprocess import PIPE, Popen
from time import sleep
import pty

master, slave = pty.openpty()
stdout = os.fdopen(master)

p = Popen(['youtube-dl', '-l', '-c', 'https://www.youtube.com/watch?v=AYlb-7TXMxM'], shell=False,stdout=slave,stderr=slave, close_fds=True)

while True:
    #line = stdout.readline().rstrip() - will strip the new line
    line = stdout.readline()
    if line != b'':
        sys.stdout.write("\r%s" % line)
        sys.stdout.flush()
    sleep(.1)

解决方案二:

  • 使用 Python 的 subprocess 模块和 multiprocessing.Queue 类来实现多线程读取。
import sys
import multiprocessing
from subprocess import PIPE, Popen

def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        queue.put(line)
    out.close()

if __name__ == '__main__':
    queue = multiprocessing.Queue()
    p = Popen(['youtube-dl', '-l', '-c', 'https://www.youtube.com/watch?v=utV1sdjr4PY'], stdout=PIPE, bufsize=1, close_fds=True)
    t = multiprocessing.Process(target=enqueue_output, args=(p.stdout, queue))
    t.daemon = True
    t.start()

    # ... do other things here

    # read line without blocking
    while True:
        try:
            line = queue.get_nowait()
        except multiprocessing.queues.Empty:
            pass
        else:
            print(line)

代码例子:

import sys, os
from subprocess import PIPE, Popen
from time import sleep
import pty

master, slave = pty.openpty()
stdout = os.fdopen(master)

p = Popen(['youtube-dl', '-l', '-c', 'https://www.youtube.com/watch?v=AYlb-7TXMxM'], shell=False,stdout=slave,stderr=slave, close_fds=True)

while True:
    #line = stdout.readline().rstrip() - will strip the new line
    line = stdout.readline()
    if line != b'':
        sys.stdout.write("\r%s" % line)
        sys.stdout.flush()
    sleep(.1)