子流程参数列表过长

时间:2018-08-30 12:05:46

标签: python linux windows python-3.x

我有一个使用subprocess调用的第三方可执行文件。不幸的是,我的参数列表太长了,并且反复调用它要比一次多次调用要慢得多。

由于多次调用命令而变慢:

def call_third_party_slow(third_party_path, files):
    for file in files:
        output = subprocess.check_output([third_party_path, "-z", file])
        if "sought" in decode(output):
            return False
    return True

快速但在有许多文件时失败:

def call_third_party_fast(third_party_path, files):
    command = [third_party_path, "-z"]
    command.extend(files) 
    output = subprocess.check_output(command)
    if "sought" in decode(output):
        return False
    return True

有什么简便的方法可以解决命令长度限制或轻松地对文件进行分组以避免超过os相关长度?

3 个答案:

答案 0 :(得分:2)

您可以像这样批量处理文件列表:

def batch_args(args, arg_max):
    current_arg_length = 0
    current_list = []
    for arg in args:
        if current_arg_length + len(arg) + 1 > arg_max:
            yield current_list
            current_list = [arg]
            current_arg_length = len(arg)
        else:
            current_list.append(arg)
            current_arg_length += len(arg) + 1
    if current_list:
        yield current_list

因此方法主体将如下所示:

os_limit = 10
for args in batch_args(files, os_limit):
    command = [third_party_path, "-z"]
    command.extend(args) 
    output = subprocess.check_output(command)
    if "sought" in decode(output):
        return False
return True

我不确定两件事:

  1. exe本身的路径是否计入限制?如果是->将其添加到每个批次的限制中。 (或将arg_max减小exe字符串的长度)
  2. args之间的空格是否计数?如果不能消除,则同时消除+1次。

将arg_max调整为可能。可能有一些方法可以针对每个OS进行查找。 Here是有关某些操作系统的最大参数大小的信息。该网站还指出Windows的上限为32k。

也许使用子流程库有更好的方法,但是我不确定。

我也不做任何异常处理(列表中的参数比最大大小长,等等)

答案 1 :(得分:0)

我通过在Windows上使用一个临时文件解决了这个问题。对于Linux,该命令可以按原样执行。

为不同平台构建完整命令的方法:

import tempfile

temporary_file = 0
def make_full_command(base_command, files):
    command = list(base_command)

    if platform.system() == "Windows":
        global temporary_file
        temporary_file = tempfile.NamedTemporaryFile()
        posix_files = map((lambda f: f.replace(os.sep, '/')),files)
        temporary_file.write(str.encode(" ".join(posix_files)))
        temporary_file.flush()
        command.append("@" + temporary_file.name)
    else:
        command.extend(files)
    return command

将文件用作全局变量可确保在执行后将其清除。

这样,我不必查找不同操作系统的最大命令长度

答案 2 :(得分:-1)

如果您不想重新发明最佳解决方案,请使用已经完全实现此目的的工具:xargs

def call_third_party_slow(third_party_path, files):
    result = subprocess.run(['xargs', '-r', '-0', third_party_path, '-z'],
        stdin='\0'.join(files) + '\0', stdout=subprocess.PIPE,
        check=True, universal_newlines=True)
    if "sought" in result.stdout:
        return False
    return True

您会注意到我也切换到subprocess.run(),该版本在Python 3.5+中可用

如果确实要重新实现xargs,则需要找到内核常量ARG_MAX的值,并构建一个命令行列表,其大小不得超过此限制。然后,您可以在每次迭代后检查输出是否包含sought,如果包含allkeys-lfu,则立即退出。