我有一个Python辅助函数来并行运行grunt命令,使用Popen
来处理子进程。目的是通过CLI进行通信。当所有这些进程需要任何用户输入时,例如,问题就开始了。文件路径,密码,'是/否'决定:
Enter file path: Enter file path: Enter file path: Enter file path: Enter file path: Enter file path: Enter file path:
Everything up-to-date
Grunt task completed successfully.
用户提供一次输入,一个进程成功完成,其他所有进程都没有完成执行。
代码:
from subprocess import check_output, Popen
def run_grunt_parallel(grunt_commands):
return_code = 0
commands = []
for command in grunt_commands:
with tempfile.NamedTemporaryFile(delete=False) as f:
app = get_grunt_application_name(' '.join(command))
commands.append({'app': app, 'process': Popen(command, stdout=f)})
while len(commands):
sleep(5)
next_round = []
for command in commands:
rc = command['process'].poll()
if rc == None:
next_round.append(command)
else:
if rc == 0:
else:
return_code = rc
commands = next_round
return return_code
有没有办法确保用户可以为每个流程提供所有必要的输入?
答案 0 :(得分:0)
你想要的只是几乎(如果不是完全)不可能。但是,如果您能够以无前缀的方式识别提示(并且,如果它有所不同,从中了解他们期望的输入线数),您应该能够管理它。
使用双向无缓冲管道运行每个进程:
Popen(command, stdin=subprocess.PIPE,
stdout=f, stderr=subprocess.PIPE, bufsize=0)
(表现良好的程序会出现标准错误。您似乎这样做了,因为尽管stdout=f
显示了提示,但是如果它们不能可靠地显示提示,您可以从管道读取好吧,在其中搜索提示,并自己将其复制到文件中。)
将所有管道设置为非阻塞。然后使用select
监视所有进程的stderr
管道。 (您可以尝试使用selectors
。)缓冲为每个进程单独阅读的内容,直到您从一个进程中识别出提示。然后显示提示(标识源进程)并接受来自用户的输入 - 如果提示之间的输出适合管道缓冲区,这不会减慢后台工作速度。将该用户输入放在与该进程关联的缓冲区中,并将其stdin
管道添加到select
。
当stdin
管道显示准备就绪时,请写入,并在完成此操作后将其从集合中删除。当从管道读取时返回EOF,join
相应的进程(如果您担心某个进程可能提前关闭它,则在SIGCHLD
处理程序中执行此操作)。
除非您有可用的select
仿真支持管道,否则您必须为每个进程使用一个线程,或者如果一个进程在写入提示后可能产生输出并且在读取之前,则必须使用每个进程一个响应。然后使用Queue
将提示作为消息发布到主线程,然后可以使用(例如)另一个进程Queue
将用户输入发送回线程(或其写作伙伴)
这适用于任何threading
- 支持平台,并且具有不依赖管道缓冲区以避免拖延健谈过程的潜在优势。