首页 文章

subprocess popen.communicate()与stdin.write()和stdout.read()

提问于
浏览
5

我注意到两种不同的行为,两种方法应该产生相同的结果 .

目标 - 使用子进程模块执行外部程序,发送一些数据并读取结果 .

外部程序是PLINK,平台是WindowsXP,Python版本3.3 .

主要想法 -

execution=["C:\\Pr..\\...\\plink.exe", "-l", username, "-pw", "***", IP]
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False)
con=a.stdout.readline()
if (con.decode("utf-8").count("FATAL ERROR: Network error: Connection timed out")==0):
   a.stdin.write(b"con rout 1\n")
   print(a.stdout.readline().decode("utf-8"))
   a.stdin.write(b"infodf\n")
   print(a.stdout.readline().decode("utf-8"))
else:
   print("ERROR")
a.kill()

到现在为止还挺好 .

现在,我想能够做一个循环(在每次写入子进程的stdin之后),等待直到子进程的stdout的EOF,打印它,然后是另一个stdin命令,依此类推 .

所以我首先尝试了之前关于相同主题的讨论(live output from subprocess commandread subprocess stdout line by linepython, subprocess: reading output from subprocess) .

并且它没有工作(它永远挂起),因为PLINK进程保持活着,直到我自己杀死它,因此没有使用等待子进程的stdout到达EOF或在stdout为真时进行循环,因为它在我杀了它之前一直都是真的 .

所以我每次写stdin时都决定从stdout读两次(对我来说很好) -

execution=["C:\\Pr..\\...\\plink.exe", "-l", username, "-pw", "***", IP]
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False)
con=a.stdout.readline()
if (con.decode("utf-8").count("FATAL ERROR: Network error: Connection timed out")==0):
   a.stdin.write(b"con rout 1\n")
   print(a.stdout.readline().decode("utf-8"))
   print(a.stdout.readline().decode("utf-8"))   //the extra line [1]
   a.stdin.write(b"infodf\n")
   print(a.stdout.readline().decode("utf-8"))
   print(a.stdout.readline().decode("utf-8"))   //the extra line [2]
else:
   print("ERROR")
a.kill()

但据我所知,第一个额外的 readline() 永远挂起,原因与我提到的相同 . 第一个额外的 readline() 永远等待输出,因为唯一的输出已经在第一个 readline() 中读取,并且因为PLINK是活的,所以函数只是"sit"并且等待新的输出行得到 .

所以我尝试了这段代码,期待同样的挂起,因为PLINK永远不会死,直到我杀了它 -

execution=["C:\\Pr..\\...\\plink.exe", "-l", username, "-pw", "***", IP]
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False)
con=a.stdout.readline()
if (con.decode("utf-8").count("FATAL ERROR: Network error: Connection timed out")==0):
   a.stdin.write(b"con rout 1\n")
   print(a.stdout.readline().decode("utf-8"))
   a.stdin.write(b"infodf\n")
   print(a.stdout.readline().decode("utf-8"))
   print(a.communicate()[0].decode("utf-8"))     //Popen.communicate() function
else:
   print("ERROR")
a.kill()

我试过这个,因为根据 communicate() 的文档,函数等到进程是 ended ,然后它就完成了 . 此外,它从stdout读取,直到 EOF . (与写入和读取stdout和stdin相同)

communicate() 完成并且没有挂起,与前一个代码块相反 .

我在这里想念的是什么?为什么当使用 communicate() 时PLINK结束,但是当使用 readline() 时却没有?

3 回答

  • 4

    你的程序没有 communicate() 死锁,因为两个进程在他们自己写更多东西之前都在等待彼此写东西 .

    communicate() 在您的示例中没有死锁,因为它关闭了流,就像命令 a.stdin.close() 那样 . 这会向你的子进程发送一个EOF,让它知道没有更多的输入,所以它可以关闭自己,然后关闭它的输出,所以 a.stdout.read() 最终返回一个EOF(空字符串) .

    There is no special signal 您的主进程将从您的子进程接收,以告知您已完成从一个命令写入结果,但已准备好进行另一个命令 .

    这意味着要像你正在尝试的那样与一个子进程来回通信, you must read the exact number of lines that the subprocess sends. 就像你看到的那样,如果你试着阅读太多行,你就会死锁 . 您可能能够 use what you know ,例如您发送它的命令,以及您目前看到的输出,以确切地知道要读取的行数 .

  • -1

    问题在于,当使用 subprocess.Popen 时,即使在进程终止之前,您的代码仍会继续被读取 . 尝试将 .wait() 附加到您的Popen电话(参见documentation),

    a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False).wait()
    

    这将确保在继续执行任何其他操作之前执行完成 .

  • -1

    您可以同时使用线程进行写入和读取,尤其是在仅需要将输出打印给用户时:

    from threading import Thread
    
    def print_remaining(stream):
        for line in stream:
            print(line.decode("utf-8"))
    
    con = a.stdout.readline()
    if "FATAL ERROR" not in con.decode("utf-8"):
        Thread(target=print_remaining, args=[a.stdout]).start()
        for cmd in LIST_OF_COMMANDS_TO_SEND:
            a.stdin.write(cmd)
    

相关问题