什么是args参数的subprocess.Popen最大长度?

问题描述 投票:14回答:2

我正在使用子进程模块中的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:Command prompt (Cmd. exe) command-line string limitation上发现了以下文章,但我不想在值中硬代码。我宁愿在运行时获取值以将命令分解为多个调用。

我在Windows XP 64上使用Python 2.6。

编辑:添加代码示例

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。我正在寻找获得args可以达到的最大长度的一般方法,因此我知道每次运行要发送多少路径。

python shell subprocess popen
2个回答
9
投票

如果您传递shell = False,则Cmd.exe不起作用。

在Windows上,子进程将使用Win32 API中的CreateProcess函数来创建新进程。此函数的documentation表明第二个参数(由subprocess.list2cmdline构建)的最大长度为32,768个字符,包括Unicode终止空字符。如果lpApplicationName为NULL,则lpCommandLine的模块名称部分限制为MAX_PATH字符。

举个例子,我建议为可执行文件(args [0])提供一个值,并为第一个参数使用args。如果我读取CreateProcess文档和子进程模块源代码是正确的,这应该可以解决您的问题。

[编辑:在我的手上Windows机器并测试后删除了args [1:]位]


1
投票

对于类Unix平台,内核常量ARG_MAXdefined by POSIX.它需要至少4096字节,但在现代系统上,它可能是一兆字节或更多。

在许多系统上,getconf ARG_MAX将在shell提示符下显示其值。

shell实用程序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()支持3.7+的text=True作为universal_newlines=True的新名称。比3.5更旧的Python版本没有run,所以你需要回归旧的遗留函数check_outputcheck_call或(很少)call

© www.soinside.com 2019 - 2024. All rights reserved.