如何在Python中处理并行运行的子进程的用户输入?

3

我有一个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

有没有办法确保用户为每个流程提供所有必要的输入?

用户为什么需要输入文件名(而不是由脚本生成)?这不会破坏自动化的目的吗? - Davis Herring
@DavisHerring 没错,这肯定会违背初衷。但提供文件名只是一个例子,这个辅助程序用于与各种CLI进行通信,我无法预测将请求什么信息。它可能是附加数据或确认信息。 - Daria
你必须知道进程期望的输入类型,这样才能将其收集到一个位置,然后传递给子进程。否则,如果一个进程要求确认用户认为来自另一个进程的内容,那么该怎么办呢? - Davis Herring
@DavisHerring 我已经澄清了问题描述 - 命令是由CLI使用的,因此我无法预测将需要什么确切的输入 - 它是否是对“是/否”问题的响应,提示输入密码短语或其他内容。我正在研究使用pexpect来监听请求中的特定单词。您认为这是个好主意吗?您有其他建议吗? - Daria
1个回答

0
你想要的几乎是不可能的(如果不是完全不可能的话)。但是,如果你能以前缀无关的方式识别提示(并且如果它有所变化,从中知道它们期望多少行输入),那么你应该能够处理它。
使用双向非缓冲管道运行每个进程:
Popen(command, stdin=subprocess.PIPE,
      stdout=f, stderr=subprocess.PIPE, bufsize=0)

良好的程序会在标准错误上提示。您的程序似乎也是这样,因为尽管有 stdout=f,但您仍然显示了提示;如果它们不能可靠地这样做,您需要从管道中读取并搜索提示,然后自己将其复制到文件中。

Unix

将所有管道设置为非阻塞状态。然后使用 select 监视所有进程的 stderr 管道。(您可以尝试使用 selectors 代替。)单独为每个进程缓冲读取的内容,直到您从其中一个进程中识别出提示。然后显示提示(标识源进程),并接受用户输入 - 如果提示之间的输出适合于管道缓冲区,则不会减慢后台工作速度。将该用户输入放入与该进程关联的缓冲区中,并将其 stdin 管道添加到 select 中。

stdin 管道准备就绪时,写入数据,并在完成后将其从集合中删除。当从管道中读取的数据返回 EOF 时,join 相应的进程(或者在 SIGCHLD 处理程序中执行此操作,如果您担心某个进程可能会提前关闭其端口)。

Windows

除非您有支持管道的select仿真器,否则您将不得不使用线程——每个进程一个线程,或者每个管道一个线程,如果一个进程在写入提示并在读取响应之前可能会产生输出。然后使用一个Queue将提示作为消息发布到主线程,主线程可以使用(例如)另一个每个进程的Queue将用户输入发送回线程(或其写作伙伴)。

这适用于任何支持threading的平台,并具有不依赖管道缓冲区以避免阻塞多话进程的潜在优势。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接