Python, ejecutando herramientas de línea de comandos en paralelo

Estoy usando Python como un lenguaje de script para hacer un procesamiento de datos y llamar a las herramientas de la línea de comandos para realizar cálculos numéricos. Deseo ejecutar herramientas de línea de comandos en paralelo, ya que son independientes entre sí. Cuando una herramienta de línea de comandos finaliza, puedo recostackr sus resultados del archivo de salida. Así que también necesito algún mecanismo de sincronización para notificar a mi progtwig principal de Python que una tarea está terminada para que el resultado pueda ser analizado en mi progtwig principal.

Actualmente, utilizo os.system() , que funciona bien para un hilo, pero no se puede poner en paralelo.

¡Gracias!

Utilice el objeto Pool desde el módulo de multiprocessing . Luego puede usar, por ejemplo, Pool.map() para hacer un parallel processing. Un ejemplo sería mi script markphotos (ver más abajo), donde una función se llama varias veces en paralelo a cada proceso de una imagen.

 #! /usr/bin/env python # -*- coding: utf-8 -*- # Adds my copyright notice to photos. # # Author: RF Smith  # $Date: 2012-10-28 17:00:24 +0100 $ # # To the extent possible under law, Roland Smith has waived all copyright and # related or neighboring rights to markphotos.py. This work is published from # the Netherlands. See http://creativecommons.org/publicdomain/zero/1.0/ import sys import subprocess from multiprocessing import Pool, Lock from os import utime, devnull import os.path from time import mktime globallock = Lock() def processfile(name): """Adds copyright notice to the file. Arguments: name -- file to modify """ args = ['exiftool', '-CreateDate', name] createdate = subprocess.check_output(args) fields = createdate.split(":") #pylint: disable=E1103 year = int(fields[1]) cr = "RF Smith  http://rsmith.home.xs4all.nl/" cmt = "Copyright © {} {}".format(year, cr) args = ['exiftool', '-Copyright="Copyright (C) {} {}"'.format(year, cr), '-Comment="{}"'.format(cmt), '-overwrite_original', '-q', name] rv = subprocess.call(args) modtime = int(mktime((year, int(fields[2]), int(fields[3][:2]), int(fields[3][3:]), int(fields[4]), int(fields[5]), 0,0,-1))) utime(name, (modtime, modtime)) globallock.acquire() if rv == 0: print "File '{}' processed.".format(name) else: print "Error when processing file '{}'".format(name) globallock.release() def checkfor(args): """Make sure that a program necessary for using this script is available. Arguments: args -- list of commands to pass to subprocess.call. """ if isinstance(args, str): args = args.split() try: with open(devnull, 'w') as f: subprocess.call(args, stderr=subprocess.STDOUT, stdout=f) except: print "Required program '{}' not found! exiting.".format(args[0]) sys.exit(1) def main(argv): """Main program. Arguments: argv -- command line arguments """ if len(argv) == 1: binary = os.path.basename(argv[0]) print "Usage: {} [file ...]".format(binary) sys.exit(0) checkfor(['exiftool', '-ver']) p = Pool() p.map(processfile, argv[1:]) p.close() if __name__ == '__main__': main(sys.argv) 

Si desea ejecutar herramientas de línea de comandos como procesos separados, simplemente use os.system (o mejor: el módulo de subprocess ) para iniciarlos de forma asíncrona. En Unix / Linux / Macos:

 subprocess.call("command -flags arguments &", shell=True) 

En Windows:

 subprocess.call("start command -flags arguments", shell=True) 

En cuanto a saber cuándo ha finalizado un comando: bajo unix, podría configurarlo con wait , etc., pero si está escribiendo los guiones de la línea de comandos, simplemente les pido que escriban un mensaje en un archivo y lo supervisen desde el llamando script de Python.

@James Youngman propuso una solución a su segunda pregunta: Sincronización. Si desea controlar sus procesos desde Python, puede iniciarlos de forma asíncrona con Popen.

 p1 = subprocess.Popen("command1 -flags arguments") p2 = subprocess.Popen("command2 -flags arguments") 

Tenga en cuenta que si utiliza Popen y sus procesos escriben una gran cantidad de datos en la salida estándar, su progtwig se bloqueará. Asegúrese de redirigir toda la salida a un archivo de registro.

p1 y p2 son objetos que puede usar para mantener un registro de sus procesos. p1.poll() no bloqueará, pero devolverá Ninguno si el proceso todavía se está ejecutando. Devolverá el estado de salida cuando haya terminado, así que puede verificar si es cero.

 while True: time.sleep(60) for proc in [p1, p2]: status = proc.poll() if status == None: continue elif status == 0: # harvest the answers else: print "command1 failed with status", status 

Lo anterior es solo un modelo: como está escrito, nunca saldrá, y seguirá “cosechando” los resultados de los procesos completados. Pero confío en que se te ocurra.