2014-10-29 99 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