python子进程和stdout

时间:2018-04-10 14:30:38

标签: python subprocess popen hang

尝试使用subprocess.Popen在单独的线程中创建运行某些bash作业(用于闪存)的python脚本。在运行时我想读取进程的标准输出,这样我就可以在一个简单的curses界面中向用户显示一些进度。所以每个线程运行如下:

def worker(panel):
    panel['status'] = 'STARTING'
    panel['process'] = Popen([SCRIPTFILE, panel['ip'], FWFILE, JADFILE, EXEFILE], stdout=PIPE, bufsize=1048576)
    panel['status'] = 'RUNNING'

    line = " "
    while line:
        line = panel['process'].stdout.readline()
        panel['status'] = line
    panel['status'] = 'DONE'

我在我的UI中看到状态在这种情况下卡在STARTING上,但是看着IP流量,它似乎实际上运行但从不返回任何输出。所以看来Popen实际上是在运行这个工作但是没有返回。一旦其他进程完成,它似乎停止挂起,我得到所有数据,因为它被缓冲。我已经看到有关从子进程读取stdout的警告,但这些警告似乎与其他方案有关。我不能真正使用communicate(),因为它等待过程完成。知道什么可以使Popen像这样挂起来吗?

试图在这里制作一个显示问题的最小版本(在Linux上运行python 2.7(如果是windows,则在脚本中更改查找路径)):

import curses
import time
from subprocess import *
import threading

def check_done(panels):
    done = True

    for panel in panels:
        if panel['status'] != 'DONE':
            done = False
            break

    return done

def flash_worker(panel):
    panel['status'] = 'STARTING'

    panel['process'] = Popen(["find", "/usr"], stdout=PIPE, bufsize=1048576)

    panel['status'] = 'RUNNING'

    line = " "

    while line:
        line = panel['process'].stdout.readline()

        panel['status'] = line

    panel['status'] = 'DONE'

def ui_worker(panels):
    stdscr = curses.initscr()
    curses.noecho()
    curses.cbreak()
    stdscr.keypad(1)
    stdscr.scrollok(1)

    stdscr.erase()
    stdscr.refresh()

    while not check_done(panels):
        y = 0

        stdscr.clear()

        stdscr.addstr(y, 0, "IP")
        stdscr.addstr(y, 15, "Status")

        height, width = stdscr.getmaxyx()

        for i in range(0, len(panels)):
            y = y + 1
            stdscr.addstr(y, 0, "%s" % (panels[i]['ip']))
            stdscr.addstr(y, 15, "%s" % (panels[i]['status']))

        stdscr.refresh()
        time.sleep(0.1)

    curses.nocbreak();
    stdscr.keypad(0);
    curses.echo()
    curses.endwin()

def main():
    panels = []

    for i in range(0,25):
        panels.append({ 'ip' : '192.168.0.%d' % (i), 'mac' : '', 'status' : '', 'thread' : None, 'success' : False })

    for panel in panels:
        panel['thread'] = threading.Thread(name='Panel %s' % (panel['ip']), target = flash_worker, args = (panel,))
        panel['thread'].start()

    try:
        ui_worker(panels)
    except KeyboardInterrupt:
        print "Caught KeyboardInterrupt, terminating..."

    for panel in panels:
        panel['thread'].join()

if __name__ == "__main__":
    main()

1 个答案:

答案 0 :(得分:0)

找到一个解决方案,而不是100%确定它的工作原理。但是,在分叉(使用Popen)时设置 close_fds = True 可以解决问题。