Какова максимальная длина подпроцесса. Откройте параметр args?
Я использую функцию 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 ответа
Если вы передаете 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-машину и протестировал]
Для 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
,