Python 子进程popen.communicate()与stdin.write()和stdout.read()的比较

Python 子进程popen.communicate()与stdin.write()和stdout.read()的比较,python,subprocess,python-3.3,Python,Subprocess,Python 3.3,我注意到两种不同的行为和两种方法应该会产生相同的结果 目标-使用子流程模块执行外部程序,发送一些数据并读取结果 外部程序是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,

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

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

外部程序是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命令,依此类推

因此,我首先尝试了之前关于同一主题的讨论得出的结果(,)

它不起作用(它永远挂起),因为PLINK进程在我自己杀死它之前一直处于活动状态,所以在stdout为真时等待子进程的stdout达到EOF或执行循环是没有用的,因为在我杀死它之前,它总是为真的

所以我决定每次给stdin写信的时候都要读两遍stdout(对我来说这是个好消息)——

但就我所知,第一个额外的
readline()
永远挂起,原因与我提到的相同。第一个额外的
readline()

所以我尝试了这个代码,期待着同样的挂起,因为在我杀死它之前,普林克是不会死的-

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()
的文档,函数会等到进程结束,然后完成。此外,它从标准输出读取,直到EOF。(与书写和阅读标准输出和标准输入相同)

但是
communicate()
完成并且不挂起,与前面的代码块相反


我错过了什么?为什么在使用
communicate()
时,PLINK会结束,而在使用
readline()
时,PLINK不会结束?

问题是,在使用
子流程时,Popen
即使在流程终止之前,您的代码也会继续被读取。尝试将
.wait()
附加到您的Popen呼叫(请参阅)


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

您的程序不会出现死锁,因为两个进程都在等待对方写东西,然后再自己写东西

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

主进程不会收到来自子进程的特殊信号,让您知道已完成写入一个命令的结果,但已准备好执行另一个命令


这意味着,要与一个子流程进行来回通信,您必须读取子流程发送的确切行数。如您所见,如果您尝试读取太多行,则会导致死锁。您可以使用您所知道的,例如您发送的命令和到目前为止看到的输出,来准确计算要读取的行数。

您可以使用线程同时写入和读取,特别是当输出只需要打印给用户时:

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)

如果您不希望打开的进程死锁,请在输入后发送EOF信号,就像您总是使用键盘a.k.a Enter(\n)发送一样。因此,通过使用它,可以避免死锁。proc.stdin.write(b'2+2\n')、proc.stdin.flush()、print(proc.stdout.readline().decode())@babygame0ver'\n'是行尾,而不是文件尾。你可以在原来的帖子中看到,他们已经在发送新行了。看起来您正在考虑一个特定的进程,它总是以每行输入一行输出的方式进行响应。情况并非总是如此。
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False).wait()
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)