Python子进程:如何使用subprocess.Popen并行执行Tee?

0 投票
1 回答
627 浏览
提问于 2025-04-30 07:24

我正在尝试写一个Python脚本,通过Arduino的命令行接口,同时将同一个hex文件编译并上传到多个微控制器上。

我的脚本做了以下几件事:

  1. 将ino文件编译成一个hex文件,放在特定的目录里。例如,
  2. 将这个hex文件上传到所有的/dev/tty.usbXXXXXX设备上。

这些是我的需求:

  • 我需要能够同时上传到多个/dev/tty.usb*设备。
  • 我想把所有子进程的标准输出和错误输出都打印到主屏幕上,格式是设备 - 标准输出/错误输出 - 消息。
  • 我想把每个子进程的标准输出和错误输出分别保存到各自的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])

我能在每个日志文件中得到我想要的内容,但我的终端屏幕上什么也没打印出来。而且在其他进程完成之前,它就结束了。

我缺少了什么呢?

暂无标签

1 个回答

0

在你的 main() 函数的最后,添加类似下面的代码:

for proc in processes:
    proc.wait()

现在,你的程序没有在等待,所以 Python 一启动所有进程就直接退出了。

顺便说一下,我不太确定用 Wrapper 对象代替真正的文件对象到底有多大用处。Python 很可能只是调用了 .fileno(),然后直接把子进程连接到那个文件描述符上。你的 .write() 方法可能根本没有被调用。如果你希望它被调用,应该用 subprocess.PIPEPopen.communicate() 来替代 Wrapper 对象和 Popen.wait()。这样你就可以根据需要使用 communicate() 的返回值(比如打印出来)。

撰写回答