代码之家  ›  专栏  ›  技术社区  ›  dheeraj tripathi

在后台运行pexpect子进程

  •  -2
  • dheeraj tripathi  · 技术社区  · 6 年前

    我有下面我正在运行的代码

    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中的命令通过从循环中获取一个输入来创建子进程,现在每次它启动子进程时,我都试图通过子进程捕获日志。read,在这种情况下,它会等待该子进程完成,然后再进入循环,如何使其在后台继续运行(我会获取动态输入的命令输入/输出日志,但不会获取此后运行的进程的日志,除非我使用read或Interactive?我使用了这个 How do I make a command to run in background using pexpect.spawn? 但它使用Interactive,它会再次等待子流程完成。。由于循环将被迭代100多次,我无法等待其中一个循环完成后再移动到另一个循环,因为pexpect中的命令是一个AWS lambda调用,所以我只需确保命令已触发,但我无法在不等待它完成的情况下捕获该调用的进程输出。。。。请告诉我你的建议

    2 回复  |  直到 6 年前
        1
  •  0
  •   abarnert    6 年前

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

    # 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)
    

    有几件事:

    从代码注释中可以看到,我假设在初始交互之后,孩子没有给我们写任何东西(并等待我们阅读)。如果不是这样的话,事情就更复杂了。

    如果您不仅想这样做,还想一次旋转8个子线程,甚至一次旋转全部子线程,那么您可以(如我的另一个答案所示)使用一个执行器或一堆线程作为初始 start_command 调用,并让这些任务/线程返回要 wait 稍后继续。例如,使用 Executor 版本,每个未来的 result() 将是一个预期的子进程。但是,您肯定需要阅读 pexpect 在这种情况下,对于某些版本的linux,在线程之间传递子进程对象可能会破坏对象。

    最后,由于您现在看到的情况比原始版本更加混乱,您可能需要更改 print 语句来显示您为哪个孩子打印(这也可能意味着更改 children 从孩子列表到 (child, path, arg) 元组等)。

        2
  •  0
  •   abarnert    6 年前

    如果希望在后台运行进程,但同时与之交互,最简单的解决方案就是启动一个线程来与进程交互。 *


    在您的情况下,听起来您正在运行数百个进程,所以您希望并行运行其中一些进程,但可能不是同时运行所有进程?如果是这样,您应该使用线程池或执行器。例如,使用 concurrent.futures 从stdlib(或 pip install 这个 futures 如果Python太旧,则返回端口):

    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)
    

    如果需要从线程化代码返回一个值(或引发异常),可能需要循环 as_completed(fs) 而不是简单的 wait . 但在这里,你似乎 print 把东西忘了,然后又忘了。

    如果 path, arg 确实是直接从这样的iterable中产生的,它通常使用起来更简单 x.map(run_command, some_iterable) .

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


    另请参见 pexpect FAQ common problems . 我不认为在当前版本中有任何问题会影响您(我们总是在一个单线程池任务中生成子线程并与之交互),但我模糊地记得过去还有一个额外的问题(与信号有关吗?)。


    *我想 asyncio 将是一个更好的解决方案,但据我所知,没有任何尝试分叉或重新实现 P预期 以非阻塞的方式完成,足以实际使用