简体   繁体   English

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

[英]subprocess popen.communicate() vs. stdin.write() and stdout.read()

I have noticed two different behaviors with two approaches that should have result in the same outcome. 我注意到两种不同的行为,两种方法应该产生相同的结果。

The goal - to execute an external program using subprocess module, send some data and read the results. 目标 - 使用子进程模块执行外部程序,发送一些数据并读取结果。

The external program is PLINK, platform is WindowsXP, Python version 3.3. 外部程序是PLINK,平台是WindowsXP,Python版本3.3。

The main idea- 主要想法 -

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

So far so good. 到现在为止还挺好。

Now, I want to be able to do a loop (after each write to the sub process's stdin), that waits untill EOF of the sub process's stdout, print it, then another stdin command, and so on. 现在,我想能够做一个循环(在每次写入子进程的stdin之后),等待直到子进程的stdout的EOF,打印它,然后是另一个stdin命令,依此类推。

So I first tried what previous discussions about the same topic yield ( live output from subprocess command , read subprocess stdout line by line , python, subprocess: reading output from subprocess ) . 所以我首先尝试了之前关于相同主题的讨论( 从子进程命令的实时输出逐行读取子进程标准输出python,子进程:从子进程读取输出 )。

And it didnt work (it hangs forever) because the PLINK process is remaining alive untill I kill it myself, so there is no use of waiting for the stdout of the sub process to reach EOF or to do a loop while stdout is true because it will always be true until I kill it. 并且它没有工作(它永远挂起),因为PLINK进程保持活着,直到我自己杀死它,因此没有使用等待子进程的stdout到达EOF或在stdout为真时进行循环,因为它在我杀了它之前一直都是真的。

So I decided to read from stdout twice every time I am writing to stdin (good enought for me)- 所以我每次写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()

But the first extra readline() hangs forever, as far as I understand, for the same reason I mentioned. 但据我所知,第一个额外的readline()永远挂起,原因与我提到的相同。 The first extra readline() waits forever for output, because the only output was already read in the first readline() , and because PLINK is alive, the function just "sit" there and waits for a new output line to get. 第一个额外的readline()会永远等待输出,因为唯一的输出已经在第一个readline()读取,并且因为PLINK是活动的,所以函数只是“坐”那里等待新的输出行得到。

So I tried this code, expecting the same hang because PLINK never dies until i kill it- 所以我尝试了这段代码,期待同样的挂起,因为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()

I tried that because according to the documentation of communicate() , the function wait until the process is ended , and then it finishes. 我试过这个,因为根据communicate()的文档,函数等到进程结束 ,然后结束。 Also, it reads from stdout until EOF . 此外,它从stdout读取直到EOF (same as writing and reading stdout and stdin) (与写入和读取stdout和stdin相同)

But communicate() finishes and does not hang, in opposite of the previous code block. 但是,就前面的代码块而言, communicate()完成并且没有挂起。

What am I missing here? 我在这里错过了什么? why when using communicate() the PLINK ends, but when using readline() it does not? 为什么当使用communicate() PLINK结束时,但是当使用readline()它不会?

Your program without communicate() deadlocks because both processes are waiting on each other to write something before they write anything more themselves. 你的程序没有communicate()死锁,因为两个进程在他们自己编写任何东西之前都在等待彼此写东西。

communicate() does not deadlock in your example because it closes the stream, like the command a.stdin.close() would. communicate()在您的示例中没有死锁,因为它关闭了流,就像a.stdin.close()命令a.stdin.close() This sends an EOF to your subprocess, letting it know that there is no more input coming, so it can close itself, which in turn closes its output, so a.stdout.read() eventually returns an EOF (empty string). 这会向你的子进程发送一个EOF,让它知道没有更多的输入,所以它可以关闭它自己,然后关闭它的输出,所以a.stdout.read()最终返回一个EOF(空字符串)。

There is no special signal that your main process will receive from your subprocess to let you know that it is done writing the results from one command, but is ready for another command. 主进程将从您的子进程接收到没有特殊信号通知您已完成从一个命令写入结果,但已为另一个命令做好准备。

This means that to communicate back and forth with one subprocess like you're trying to, you must read the exact number of lines that the subprocess sends. 这意味着要像您正在尝试的那样与一个子进程来回通信, 您必须读取子进程发送的确切行数。 Like you saw, if you try to read too many lines, you deadlock. 就像你看到的那样,如果你试着阅读太多行,你会陷入僵局。 You might be able to use what you know , such as the command you sent it, and the output you have seen so far, to figure out exactly how many lines to read. 您可能能够使用您所知道的内容 (例如您发送的命令)和您目前看到的输出,以确切地知道要读取的行数。

The thing is that when using subprocess.Popen your code continues to be read even before the process terminates. 问题在于,当使用subprocess.Popen时,即使在进程终止之前,您的代码仍会继续被读取。 Try appending .wait() to your Popen call (see documentation ), 尝试将.wait()附加到您的.wait()调用(参见文档 ),

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

This will ensure that the execution finishes before going on with anything else. 这将确保在继续执行任何其他操作之前执行完成。

You can use threads to write and read at the same time, especially if the output only needs to be printed to the user: 您可以同时使用线程进行写入和读取,尤其是在仅需要将输出打印给用户时:

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM