【问题标题】:What is the subprocess.Popen max length of the args parameter?args 参数的 subprocess.Popen 最大长度是多少?
【发布时间】:2011-01-23 18:52:17
【问题描述】:

我正在使用子进程模块中的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 的最大长度,以便我知道每次运行要发送多少条路径。

【问题讨论】:

  • 您能否提供一个示例值来说明您为 args 提供的值?
  • 我参加聚会很晚了,但我想补充一点,由于我的 PATH 环境变量在添加许多条目后变得太长,所以我遇到了同样的错误。

标签: python shell subprocess popen


【解决方案1】:

如果您传递 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:] 位]

【讨论】:

  • 我不确定我是否遵循您关于使用 args[1:] 作为第一个参数的建议。我已经用代码示例更新了我的问题。为 CreateProcess 上的链接和提示 +1
  • 我试过了,但仍然遇到了限制:subprocess.Popen(cmd[1:] + paths,executable=cmd[0],stdout=subprocess.PIPE)。现在我使用 32000 作为命令长度的限制,并多次调用我的命令并收集所有输出。我希望能够不进行 32000 硬编码,而是从环境中获取该值。
  • 正如我在引用的文档中提到的那样,32768 限制是硬编码在 CreateProcess 原语中的(这是 16 位有符号整数的上限,即 215)。由于 list2cmd 在构建命令行时会添加引号和空格,因此您将在 sum([len(a) for a in args]) 达到 215 之前达到该限制。没有办法使用通配符将参数传递给可执行文件吗? (通配符一般由windows下的可执行文件处理)
  • 好点。我正在考虑空格,但没有想到引号。我使用 32000 作为我的硬编码限制,它必须为空间留出足够的空间。在实践中,我看到每次运行都适合约 190 个文件(总共大约 4k 个文件)。有些在同一个目录中,但大多数分布在多个目录中。现在我只是把硬编码限制留在里面。
  • 好吧,Windows 上的限制是硬编码为 2**15,而在该操作系统的 64 位版本上可能就是这种情况。在 posix 系统上,没有限制,除了你的 RAM:Popen 使用 execvp 或 execvpe,它使用 NULL 终止的 char* 数组作为没有大小限制的参数。
【解决方案2】:

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

在许多系统上,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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-20
  • 1970-01-01
  • 2015-11-24
  • 2011-09-22
  • 2016-07-13
  • 2014-07-22
相关资源
最近更新 更多