2016-04-11 75 views
0

我試圖讓subprocess.Popen()正常工作,但由於某種原因返回的值是完全錯誤的。如何讓subprocess.Popen正常工作?

該腳本將打開一個FTP連接腳本,用於從服務器下載文件,然後返回成功和未成功下載文件的元組。此腳本在使用subprocess.call()之前已工作,但我想使用Popen(),以便它所調用的腳本位於另一個線程中,並且不會干擾主程序。

這裏是我的主類:

def FTPDownload(self): 
    try: 
     ftpReq = subprocess.Popen(['Python', mw._['cwd']+"dwnldMedia.py"], 
            shell=True, 
            stdout=subprocess.PIPE) 
     successful, unsuccessful = ftpReq.communicate() 
     self.consPrompt("Successful:\t"+str(successful)) 
     self.consPrompt("Unsuccessful:\t"+str(unsuccessful)) 
    except subprocess.CalledProcessError as e: 
     self.consPrompt((cp._['E0']).format(str(e))) 

,這裏是dwnldMedia.py__init__電話download()):

def download(self): 
    #print("connected") 
    self.server = FTP(**self.serverDetails) 
    self.server.login(**self.userDetails) 

    self.server.cwd("/public_html/uploads") #changing to /pub/unix 
    #print "File List: \n" 
    files = [] 
    successful = [0] 
    unsuccessful = [0] 
    self.server.retrlines("NLST",files.append) 
    for f in files: 
     if(f != '.' and f != '..'): 
      #print("downloading:\t"+f) 
      local_filename = os.path.join(mw._['cwd']+"media", f) 
      with open(local_filename, "wb") as i: 
       self.server.retrbinary("RETR " + f, i.write) 
       #print("\t| Success") 
       successful.append(f) 
    for f in files: 
     if(f != '.' and f != '..' and f not in successful): 
      unsuccessful.append(f) 
    return (successful, unsuccessful) 

我得到的輸出是:

Successful: 
Unsuccessful: None 

successful有值爲None

+2

['Popen.communicate()'](https://docs.python.org/2/庫/ subprocess.html#subprocess.Popen。溝通)從過程返回'stdout'和'stderr'的內容 - 而不是你的'download()'方法_returns_。換句話說,你需要將'成功'和'不成功'的值寫入'sys.stdout'。這樣做的一種方法是簡單地將它們打印出來。 – martineau

+1

如果你期望任何數據通過錯誤流,我建議你添加一個'stderr = subprocess.PIPE'。 –

+0

@馬蒂諾,請張貼您的答案作爲答案。 –

回答

0

如果你真的工作使用subprocess.call()了東西,你還不如只是一味地使用它 - 因爲call()使用Popen()內部 - 所以dwnldMedia.py已經正在運行作爲一個獨立的子過程(你一個新的名爲線程),因此代碼執行的方面不會通過直接調用Popen()來更改。

無論您使用call()Popen() + communicate(),因爲這兩個等待腳本完成後再繼續執行下載不會同時出現(我以爲是你的目標)。對於併發下載,您需要使用multiprocessing模塊進行多任務處理。由於您所做的是I/O綁定,因此併發下載也可以使用thread和/或threading模塊完成(因爲它們都在同一進程內,因此共享數據通常更簡單)。這就是說,所以這實際上是對你的問題的回答,下面是如何使用從subprocess.communicate()返回的結果並將數據從一個進程傳遞到另一個進程。您不能簡單地將return結果從一個進程轉移到另一個進程,因爲它們位於不同的地址空間中。一種方法是在它們之間「管」數據。 communicate()收集所有收到的數據,並在返回時將其作爲兩個字符串的元組返回,其中一個用於stderr,另一個用於stderr

該示例使用pickle將發送的數據轉換爲可在接收端的Python對象中返回的內容。 json模塊工作得很好。我不得不從你的問題中的例子中刪除大量的代碼,以使我可以運行和測試的東西,但試圖保持下面的內容完整的結構。

import cPickle as pickle 
import subprocess 

class SomeClass(object): 
    def FTPDownload(self): 
     try: 
      # The -u argument puts stdin, stdout and stderr into binary mode 
      # (as well an makes them unbuffered). This is needed to avoid 
      # an issue with writing pickle data to streams in text mode 
      # on Windows. 
      ftpReq = subprocess.Popen(['python', '-u', 'dwnldMedia.py'], 
             stdout=subprocess.PIPE, 
             stderr=subprocess.PIPE) 
      stdout, stderr = ftpReq.communicate() 
      if stdout: 
       # convert object returned into a Python obj 
       results = pickle.loads(stdout) 
       print(' successful: {successful}'.format(**results)) 
       print('unsuccessful: {unsuccessful}'.format(**results)) 
      if stderr: 
       print("stderr:\n{}".format(stderr)) 
     except subprocess.CalledProcessError as exception: 
      print('exception: {}'.format(str(exception))) 

if __name__ == '__main__': 
    instance = SomeClass() 
    instance.FTPDownload() 

和這裏的download()方法在dwnldMedia.py腳本一個精簡版:

import cPickle as pickle 
from random import randint # for testing 
import os 

# needed if not run in -u mode 
#if os.name == 'nt': # put stdout into binary mode on Windows 
# import sys, msvcrt 
# msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) 

class OtherClass(object): 
    def __init__(self, files): 
     self.files = files 
     self.download() 

    def download(self): 
     files = [fn for fn in self.files if fn != '.' and fn != '..'] 
     successful = [] 
     unsuccessful = [] 
     for fn in files: 
      if randint(0, 1) % 2: # simulate random download success 
       successful.append(fn) 
     for fn in files: 
      if fn not in successful: 
       unsuccessful.append(fn) 
     results = { # package lists into single object 
      'successful': successful, 
      'unsuccessful': unsuccessful 
     } 
     print(pickle.dumps(results)) # send object by writing it to stdout 

instance = OtherClass(['.', '..', 'file1', 'file2', 'file3', 'file4']) 
+0

我試過使用多重處理,但不是運行目標腳本,它只是複製我的GUI窗口'ftp = multiprocessing.Process(name =「FTP download」,target = mw ._ ['cwd'] +「dwnldMedia.py 「) (其中mw ._ ['cwd']是當前工作目錄) –

+0

'multiprocessing'可能會非常棘手。使用它的規則之一 - 聽起來像你可能沒有遵循 - 是主腳本必須在表示根進程的代碼部分附近有一個'if __name__ =='__main __':'guard (因爲主腳本是由子進程導入的)。 – martineau