Список аргументов подпроцесса к длинному - PullRequest
0 голосов
/ 30 августа 2018

У меня есть сторонний исполняемый файл, вызываемый с помощью subprocess.check_output, к сожалению, мой список аргументов слишком длинный, и его неоднократный вызов намного медленнее, чем вызов его один раз со многими аргументами.

Медленно из-за многократного вызова команды:

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

Есть ли какой-нибудь простой способ обойти ограничение длины команды или легко сгруппировать файлы, чтобы избежать превышения длины, зависящей от ОС?

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Если вы не хотите изобретать оптимальное решение, используйте инструмент, который уже реализует именно это: 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, и немедленно завершать работу, если это так.

0 голосов
/ 11 сентября 2018

Я решил это с помощью временного файла в 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

Использование файла в качестве глобальной переменной обеспечивает его очистку после выполнения.

Таким образом, мне не нужно было искать максимальную длину команды для разных ОС

0 голосов
/ 30 августа 2018

Вы можете группировать список файлов следующим образом:

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. Считается ли расстояние между аргументами? Если нет, удалите оба +1 случая.

Настройте arg_max на то, что возможно. Вероятно, есть какой-то способ выяснить это для каждой ОС. Здесь - это некоторая информация о максимальном размере аргументов некоторых ОС. На этом сайте также говорится, что для Windows существует ограничение в 32 КБ.

Может быть, есть лучший способ сделать это с помощью библиотеки подпроцессов, но я не уверен.

Также я не занимаюсь обработкой исключений (аргументы в списке длиннее максимального размера и т. Д.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...