我使用Python作为脚本语言来进行数据处理并调用命令行工具进行数字运算。我希望并行运行命令行工具,因为它们彼此独立。当一个命令行工具完成后,我可以从输出文件中收集其结果。所以我还需要一些同步机制来通知我的主Python程序一个任务完成,以便结果可以解析到我的主程序中。
目前,我使用os.system()
,它适用于单线程,但不能并行化。
谢谢!
答案 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,您的程序将会死锁。务必将所有输出重定向到日志文件。
p1
和p2
是可用于密切关注流程的对象。 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)