我注意到两种不同的行为,两种方法应该产生相同的结果。
目标 - 使用子流程模块执行外部程序,发送一些数据并读取结果。
外部程序是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 command,read subprocess stdout line by line,python, 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是活的,所以函数只是“坐”那里并等待新的输出行到得到。
所以我尝试了这段代码,期待同样的挂起,因为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()
的文档,函数会一直等到结束,然后就完成了。此外,它从stdout读取,直到 EOF 。 (与写和读stdout和stdin相同)
但是communicate()
完成并且没有挂起,与前一个代码块相反。
我在这里缺少什么?为什么在使用communicate()
PLINK结束时,但在使用readline()
时却没有?
答案 0 :(得分:4)
你的程序没有communicate()
死锁,因为两个进程在他们自己写更多东西之前都在等待彼此写东西。
communicate()
在您的示例中没有死锁,因为它会关闭流,就像命令a.stdin.close()
一样。这会向你的子进程发送一个EOF,让它知道没有更多的输入,所以它可以关闭它自己,然后关闭它的输出,所以a.stdout.read()
最终返回一个EOF(空字符串)。
主进程将从您的子进程接收到没有特殊信号,以告知您已完成从一个命令写入结果,但已准备好接受另一个命令。
这意味着要与您尝试的一个子进程来回通信,您必须读取子进程发送的确切行数。如您所见,如果您尝试阅读线路太多,你僵局了。您可以使用您所知道的,例如您发送的命令,以及您目前看到的输出,以确切地知道要读取的行数。
答案 1 :(得分:-1)
问题在于,当使用subprocess.Popen
时,即使在进程终止之前,您的代码仍会继续被读取。尝试将.wait()
附加到您的Popen电话(请参阅documentation),
a=subprocess.Popen(execution, bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False).wait()
这将确保在继续执行任何其他操作之前执行完毕。
答案 2 :(得分:-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)