簡體   English   中英

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

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

我注意到兩種不同的行為,兩種方法應該產生相同的結果。

目標 - 使用子進程模塊執行外部程序,發送一些數據並讀取結果。

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

所以我首先嘗試了之前關於相同主題的討論( 從子進程命令的實時輸出逐行讀取子進程標准輸出python,子進程:從子進程讀取輸出 )。

並且它沒有工作(它永遠掛起),因為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()它不會?

你的程序沒有communicate()死鎖,因為兩個進程在他們自己編寫任何東西之前都在等待彼此寫東西。

communicate()在您的示例中沒有死鎖,因為它關閉了流,就像a.stdin.close()命令a.stdin.close() 這會向你的子進程發送一個EOF,讓它知道沒有更多的輸入,所以它可以關閉它自己,然后關閉它的輸出,所以a.stdout.read()最終返回一個EOF(空字符串)。

主進程將從您的子進程接收到沒有特殊信號通知您已完成從一個命令寫入結果,但已為另一個命令做好准備。

這意味着要像您正在嘗試的那樣與一個子進程來回通信, 您必須讀取子進程發送的確切行數。 就像你看到的那樣,如果你試着閱讀太多行,你會陷入僵局。 您可能能夠使用您所知道的內容 (例如您發送的命令)和您目前看到的輸出,以確切地知道要讀取的行數。

問題在於,當使用subprocess.Popen時,即使在進程終止之前,您的代碼仍會繼續被讀取。 嘗試將.wait()附加到您的.wait()調用(參見文檔 ),

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM