使用subprocess.communicate()来管道stdin而无需等待进程

时间:2016-03-02 22:15:31

标签: python

我需要启动一个进程并将一个字符串传递给stdin,我目前正在这样做:

proc = subprocess.Popen(["MyCommandHere"], stdin=subprocess.PIPE)
proc.communicate(input=bytes(my_str_input + "\n", "ascii"))

问题在于,当我使用subprocess.communicate()时,它是一个阻塞调用,等待进程退出。我不想等。

有没有办法让communicate()不阻止,或者用其他方式管道我的输入?我问的是非阻塞写入,而不是非阻塞读取。

2 个答案:

答案 0 :(得分:1)

两个明显的选择:

  1. 使用单独的主题或流程
  2. 从临时文件中提取stdin
  3. 选项1:

    import threading
    
    def send_data_to(proc, inp):
        proc.communicate(inp)
    
    proc = subprocess.Popen(["MyCommandHere"], stdin=subprocess.PIPE)
    threading.Thread(target=send_data_to, args=(proc, bytes(my_str_input + "\n", "ascii"))).start()
    

    选项2:

    import tempfile
    
    with tempfile.TemporaryFile() as tf:
        tf.write(bytes(my_str_input + "\n", "ascii"))
        tf.flush()
        tf.seek(0)  # Might not be needed
        proc = subprocess.Popen(["MyCommandHere"], stdin=tf)
    

    对临时文件的写入可以阻止,但通常由OS优化临时文件,以尽可能减少对磁盘的写入;如果这个过程可能需要一些时间才能完成,你可能会直接阻塞过长的管道,但是用于写出数据的小阻塞并不重要。即使Python在with块退出时关闭临时文件(这通常会导致它被删除),该进程会保留一个句柄,防止它在进程完成之前被清除。

    注意:所有这些都假设在启动时该过程可能不会立即完全消耗您的输入。如果该过程基本上立即读取输入,那么它的所有工作都可以完成,您可以简化为:

    proc.stdin.write(bytes(my_str_input + "\n", "ascii"))
    proc.stdin.close()  # Ensures the process knows nothing else is coming
    

    如果进程一次消耗一点输入,并且输入大于管道缓冲区(因此你不能一次写入所有内容),这就冒了阻塞的风险。

答案 1 :(得分:0)

看看at the docs on Popen.stdin。它只是一个标准的可写对象(在大多数情况下,无论如何都是标准的文件句柄),所以你可以这样做:

proc.stdin.write(bytes(...))

将数据写入stdin而无需等待子进程完成。

相关问题