2014-10-29 76 views
0

我正在嘗試編寫一個python腳本,通過Arduino的命令行界面來並行編譯和上傳同一個hex文件給多個微控制器。Python子流程:用subprocess.Popen並行執行復制Tee?

我的腳本執行以下操作:

  1. 編譯鮑文件爲十六進制文件在一個特定的目錄中。例如,
  2. 將十六進制上傳到所有/dev/tty.usbXXXXXX。

這些要求:

  • 我必須能夠上傳到多個/dev/tty.usb*並行。
  • 我想打印全部來自我所有的subprocess.Popen的輸出和錯誤到我的主屏幕設備 - STDOUT/STDERR - 味精作爲格式
  • 我想從每個POPEN標準輸出和標準錯誤保存到自己的tty.usb *日誌文件。

現在我得:

import errno 
import os 
import re 
import subprocess 

ARDUINO_EXECUTABLE = '/Applications/Arduino.app/Contents/MacOS/JavaApplicationStub' 
HEX_FOLDER_DIR = '/tmp/oyoroi' 
LOG_FOLDER_DIR = './logs' 


def get_devices(): 
    """Returns a list of tty.usbXXXXXX 
    Make sure you use the USB hub. This will force an extra character in the /dev/tty.usbXXXXXX 
    """ 
    ret = [] 
    for device in os.listdir('/dev'): 
    if re.match('tty.usbmodem[0-9]{6}', device): 
     ret.append(device) 
    return ret 


class Wrapper(object): 
    """Wrapper for file objects 
    """ 
    def __init__(self, name, fobject): 
    self.name = name 
    self.fobject = fobject 

    def fileno(self): 
    return self.fobject.fileno() 

    def flush(self): 
    self.fobject.flush() 

    def write(self, a_str): 
    print self.name, a_str 
    self.fobject.write(a_str) 


def main(fname): 
    """Build once, but upload in parallel 
    """ 
    try: 
    os.makedirs(HEX_FOLDER_DIR) 
    except OSError as exc: 
    if exc.errno == errno.EEXIST and os.path.isdir(HEX_FOLDER_DIR): 
     pass 

    fname = os.path.abspath(fname) 

    # Builds the hex 
    command = "%s --verify --pref build.path=%s %s" % (ARDUINO_EXECUTABLE, HEX_FOLDER_DIR, fname) 
    print "(Build Command)", command 
    proc = subprocess.call(command, shell=True) 

    # Make the log directory 
    try: 
    os.makedirs(LOG_FOLDER_DIR) 
    except OSError as exc: 
    if exc.errno == errno.EEXIST and os.path.isdir(LOG_FOLDER_DIR): 
     # delete folder 
     import shutil 
     shutil.rmtree(LOG_FOLDER_DIR) 
     # and recreate again 
     os.makedirs(LOG_FOLDER_DIR) 

    # Upload in parallel 
    devices = get_devices() 
    processes = [] 

    for device in devices: 
    device_path = '/dev/' + device 
    log_file_path = os.path.join(LOG_FOLDER_DIR, device + '.log') 
    with open(log_file_path, 'a') as logfile: 
     command = "%s --upload --pref build.path=%s --port %s %s" % \ 
       (ARDUINO_EXECUTABLE, HEX_FOLDER_DIR, device_path, fname) 
     print "(Upload Command)", command 

     wstdout = Wrapper('%_STDOUT', logfile) 
     wstderr = Wrapper('%_STDERR', logfile) 
     proc = subprocess.Popen(command, shell=True, stdout=wstdout, stderr=wstderr) 
     processes.append(proc) 


if __name__ == "__main__": 
    import sys 
    if len(sys.argv) != 2: 
    print "python upload.py <.ino>" 
    exit(1) 
    main(sys.argv[1]) 

我能夠得到我想要在每個日誌文件,但我的終端不打印屏幕上的任何內容。在其他過程完成之前它也結束了。

我錯過了什麼?

+0

'stdout = wstdout'不正確。 'subprocess.Popen()'不接受類似文件的對象(除了'.fileno()'以外的所有Wrapper方法在你的情況下被忽略,這就是爲什麼你什麼都看不到 - 沒有任何內容被打印出來('print self .name,a_str'不被調用))。它需要一個真實的文件(真實文件描述符)。請參閱[如何實現使用多線程的teed_call()](http://stackoverflow.com/a/4985080/4279)。這裏是[如何獨立收集stdout/stderr並將其打印到單個線程中的控制檯](http://stackoverflow.com/a/25960956/4279)。 – jfs 2014-10-29 16:07:28

回答

0

在你main()末,添加如下代碼:

for proc in processes: 
    proc.wait() 

現在,你是不是在等待,所以Python就全部工序均由推出退出。

爲了記錄,我不完全確定通過Wrapper對象而不是真正的文件對象是多麼有用。 Python很可能只是調用.fileno(),然後直接將子進程附加到該文件描述符。您的.write()方法可能未被調用。如果您需要撥打電話,則應使用subprocess.PIPEPopen.communicate()來代替Wrapper對象和Popen.wait()。然後,您可以以任何您認爲合適的方式(即打印它)使用返回值communicate()

+0

所以我試着做這樣的事情:https://gist.github.com/vicngtor/40d9cb41780a0d5f14e8最後沒有打印。我錯過了什麼嗎? – Sparrowcide 2014-10-29 02:31:53

+0

'communic()'只有在你將'subprocess.PIPE'作爲'stdout'和/或'stderr'(然後只傳遞給你的流)傳遞時纔有效。在這種情況下,我相信你需要使用'communications()'返回值來手動'write()'到日誌文件和'print()'到屏幕上。 – Kevin 2014-10-29 02:37:41