运行 预期子进程在后台

Running pexpect subprocesses in background

我有下面的代码,我是 运行

try:
    child = pexpect.spawn(
        ('some command --path {0}  somethingmore --args {1}').format(
            <iterator-output>,something),
        timeout=300)
    child.logfile = open(file_name,'w')
    child.expect('x*')
    child.sendline(something)
    child.expect('E*')
    child.sendline(something))
   #child.read()
    child.interact()
    time.sleep(15)
    print child.status
except Exception as e:
    print "Exception in child process"
    print str(e)

现在,pexpect 中的命令通过从循环中获取输入之一来创建子进程,现在每次它启动一个子进程时,我都会尝试通过 child.read 捕获日志,在这种情况下,它会等待为了在再次进入循环之前完成该子进程,我如何让它在后台保持 运行 (我得到我动态输入的命令 input/output 的日志,但不是进程的除非我使用读取或交互,否则此后运行?我使用这个 How do I make a command to run in background using pexpect.spawn? 但它使用交互再次等待该子进程完成.. 因为循环将被迭代 alomst 超过 100 次我不能等待一个到在移动到其他之前完成,因为 pexpect 中的命令是 AWS lambda 调用,我需要确保命令被触发但我无法在不等待它完成的情况下捕获该调用的过程输出...... . 请让我知道您的建议

如果你想 运行 一个进程在后台,但同时与它交互,最简单的解决方案是只启动一个线程与进程交互。 *


在您的情况下,听起来您正在 运行 处理数百个进程,因此您想 运行 并行处理其中的一些进程,但也许不是同时处理所有进程?如果是这样,您应该使用线程池或执行程序。例如,使用 stdlib 中的 concurrent.futures(如果您的 Python 太旧,则使用 pip install futures 向后移植):

def run_command(path, arg):
    try:
        child = pexpect.spawn(('some command --path {0}  somethingmore --args {1}').format(path, arg), timeout=300)
        child.logfile = open(file_name,'w')
        child.expect('x*')
        child.sendline(something)
        child.expect('E*')
        child.sendline(something))
        # child.read()
        child.interact()
        time.sleep(15)
        print child.status
    except Exception as e:
        print "Exception in child process"
        print str(e)

with concurrent.futures.ThreadPoolExecutor(max_workers=8) as x:
    fs = []
    for path, arg in some_iterable:
        fs.append(x.submit(run_command, path, arg))
    concurrent.futures.wait(fs)

如果您需要从线程代码中 return 一个值(或引发异常),您可能需要一个循环遍历 as_completed(fs) 而不是简单的 wait。但在这里,你似乎只是 print 把东西弄出来然后忘记了。

如果 path, arg 确实像这样直接从可迭代对象中产生,通常使用 x.map(run_command, some_iterable).

会更简单

所有这些(以及其他选项)在模块文档中都有很好的解释。


另见 pexpect FAQ and common problems。我不认为在当前版本中有任何问题会影响您(我们总是在单个 thread-pooled 任务中生成子对象并与其完全交互),但我依稀记得曾经有一个过去的其他问题(与信号有关?)。


* 我认为 asyncio 会是一个更好的解决方案,除了据我所知 none 尝试分叉或重新实现 pexpect非阻塞方式足够完整,可以实际使用……

如果您实际上不想与许多并行进程交互,而是想与每个进程短暂交互,那么在它运行时忽略它并继续与下一个进程交互......

# Do everything up to the final `interact`. After that, the child
# won't be writing to us anymore, but it will still be running for
# many seconds. So, return the child object so we can deal with it
# later, after we've started up all the other children.
def start_command(path, arg):
    try:
        child = pexpect.spawn(('some command --path {0}  somethingmore --args {1}').format(path, arg), timeout=300)
        child.logfile = open(file_name,'w')
        child.expect('x*')
        child.sendline(something)
        child.expect('E*')
        child.sendline(something))
        # child.read()
        child.interact()
        return child
    except Exception as e:
        print "Exception in child process"
        print str(e)

# First, start up all the children and do the initial interaction
# with each one.
children = []
for path, args in some_iterable:
    children.append(start_command(path, args))

# Now we just need to wait until they're all done. This will get
# them in as-launched order, rather than as-completed, but that
# seems like it should be fine for your use case.
for child in children:
    try:
        child.wait()
        print child.status
    except Exception as e:
        print "Exception in child process"
        print str(e)

一些事情:

从代码注释中注意到,我假设 child 在初始交互后没有向我们写任何东西(并等待我们阅读)。如果不是这样,事情就有点复杂了。

如果你不仅想这样做,而且想一次旋转 8 children,甚至一次旋转所有,你可以(如我的其他答案所示)使用执行器或者只是用于初始 start_command 调用的一堆线程,并让那些 tasks/threads return 和 child object 稍后被 waited .例如,对于 Executor 版本,每个未来的 result() 将是一个预期的 child 进程。但是,在这种情况下,您肯定需要阅读有关线程的 pexpect 文档——对于某些版本的 linux,在线程之间传递 child-process object 可能会破坏 objects.

最后,由于您现在看到的东西 out-of-order 比原始版本多得多,您可能需要更改 print 语句以显示您正在打印的 child for(这也可能意味着将 children 从 children 的列表更改为 (child, path, arg) 元组等的列表)。