Python,并行运行命令行工具

时间:2012-03-04 11:35:48

标签: python shell command-line parallel-processing

我使用Python作为脚本语言来进行数据处理并调用命令行工具进行数字运算。我希望并行运行命令行工具,因为它们彼此独立。当一个命令行工具完成后,我可以从输出文件中收集其结果。所以我还需要一些同步机制来通知我的主Python程序一个任务完成,以便结果可以解析到我的主程序中。

目前,我使用os.system(),它适用于单线程,但不能并行化。

谢谢!

2 个答案:

答案 0 :(得分:9)

如果要将命令行工具作为单独的进程运行,只需使用os.system(或更好:subprocess模块)以异步方式启动它们。在Unix / linux / macos上:

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

在Windows上:

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

至于知道命令何时完成:在unix下你可以设置wait等,但是如果你正在编写命令行脚本,我只是让他们在文件中写一条消息,并从调用python脚本监视文件。

@James Youngman提出了第二个问题的解决方案:同步。如果你想从python控制你的进程,你可以用Popen异步启动它们。

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

请注意,如果您使用Popen并且您的进程将大量数据写入stdout,您的程序将会死锁。务必将所有输出重定向到日志文件。

p1p2是可用于密切关注流程的对象。 p1.poll()不会阻止,但如果进程仍在运行,则返回None。完成后它将返回退出状态,因此您可以检查它是否为零。

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

以上只是一个模型:正如所写的那样,它永远不会退出,它将继续“收获”已完成流程的结果。但我相信你明白了。

答案 1 :(得分:9)

使用Pool模块中的multiprocessing对象。然后你可以使用例如Pool.map()进行并行处理。一个例子是我的markphotos脚本(见下文),其中一个函数被调用多次并行处理图片。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Adds my copyright notice to photos.
#
# Author: R.F. Smith <rsmith@xs4all.nl>
# $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 = "R.F. Smith <rsmith@xs4all.nl> 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)