Какова максимальная длина подпроцесса. Откройте параметр args? - PullRequest
14 голосов
/ 04 марта 2010

Я использую функцию Popen из модуля подпроцесса для запуска инструмента командной строки:

subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

Инструмент, который я использую, берет список файлов, которые он затем обрабатывает. В некоторых случаях этот список файлов может быть очень длинным. Есть ли способ найти максимальную длину параметра args? При передаче большого количества файлов в инструмент появляется следующая ошибка:

Traceback (most recent call last):
  File "dump_output_sopuids.py", line 68, in <module>
    uid_map = create_sopuid_to_path_dict_dcmdump(dicom_files)
  File "dump_output_sopuids.py", line 41, in create_sopuid_to_path_dict_dcmdump
    dcmdump_output = subprocess.Popen(cmd,stdout=subprocess.PIPE).communicate(0)[0]
  File "c:\python26\lib\subprocess.py", line 621, in __init__
    errread, errwrite)
  File "c:\python26\lib\subprocess.py", line 830, in _execute_child
    startupinfo)
WindowsError: [Error 206] The filename or extension is too long

Есть ли общий способ найти эту максимальную длину? Я нашел следующую статью о msdn: Ограничение строки командной строки в командной строке (Cmd. Exe) , но я не хочу жестко кодировать значение. Я бы предпочел получить значение во время выполнения, чтобы разбить команду на несколько вызовов.

Я использую Python 2.6 в Windows XP 64.

Редактировать: пример добавления кода

paths = ['file1.dat','file2.dat',...,'fileX.dat']
cmd = ['process_file.exe','+p'] + paths
cmd_output = subprocess.Popen(cmd,stdout=subprocess.PIPE).communicate(0)[0]

Проблема возникает из-за того, что каждая действительная запись в списке paths обычно представляет собой очень длинный путь к файлу И их несколько тысяч.

Я не против разбить команду на несколько вызовов process_file.exe. Я ищу общий способ получения максимальной длины аргументов, поэтому я знаю, сколько путей нужно отправить для каждого прогона.

Ответы [ 2 ]

9 голосов
/ 04 марта 2010

Если вы передаете shell = False, то Cmd.exe не входит в игру.

В Windows подпроцесс будет использовать функцию CreateProcess из Win32 API для создания нового процесса. Документация для этой функции гласит, что второй аргумент (который создается subprocess.list2cmdline) имеет максимальную длину 32 768 символов, включая завершающий нулевой символ Unicode. Если lpApplicationName равно NULL, часть имени модуля lpCommandLine ограничена символами MAX_PATH.

Учитывая ваш пример, я предлагаю предоставить значение для исполняемого файла (args [0]) и использовать args для первого параметра. Если мое чтение документации CreateProcess и исходного кода модуля подпроцесса является правильным, это должно решить вашу проблему.

[править: убрал бит args [1:] после того, как я заполучил Windows-машину и протестировал]

1 голос
/ 02 марта 2019

Для Unix-подобных платформ константа ядра ARG_MAX равна , определенной POSIX. Требуется не менее 4096 байт, хотя в современных системах это, вероятно, мегабайт или более.

Во многих системах getconf ARG_MAX покажет свое значение в приглашении оболочки.

Утилита оболочки xargs удобно позволяет разбить длинную командную строку. Например, если

python myscript.py *
Сбой

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

printf '%s\0' * |
xargs -0 python myscript.py

(Опция -0 является расширением GNU, но на самом деле является единственным полностью безопасным способом однозначно передать список имен файлов, который может содержать символы новой строки, символы кавычек и т. Д.) Может также изучить

find . -maxdepth 1 -type f -exec python myscript.py {} +

И наоборот, передать длинный список аргументов subprocess.Popen() и друзьям, что-то вроде

p = subprocess.Popen(['xargs', '-0', 'command'],
    stdin=subprocess.PIPE, stdout=subprocess.PIPE,
    stderr=subprocess.PIPE)
out, err = p.communicate('\0'.join(long_long_argument_list))

... где в большинстве сценариев вам, вероятно, следует избегать необработанного Popen() и позволить функции-оболочке, такой как run() или check_call(), выполнять большую часть работы:

r = subprocess.run(['xargs', '-0', 'command'],
    input='\0'.join(long_long_argument_list),
    universal_newlines=True)
out = r.stdout

subprocess.run() поддерживает text=True в 3.7+ как новое имя universal_newlines=True. В старых версиях Python, отличных от 3.5, не было run, поэтому вам нужно вернуться к старым устаревшим функциям check_output, check_call или (редко) call.

...