【发布时间】:2018-09-20 05:30:00
【问题描述】:
尝试使用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 实际运行了这项工作但没有返回。一旦其他进程完成,它似乎就会停止挂起,我会在缓冲时立即获取所有数据。我已经看到有关在子进程上从标准输出读取的警告,但这些警告似乎与其他场景有关。而且我不能真正使用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()
【问题讨论】:
-
不确定是不是同样的问题,但我会尝试解决方案。
标签: python subprocess popen freeze