我正在使用子进程模块中的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最大长度的一般方法,因此我知道每次运行要发送多少路径。
答案 0 :(得分: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 :(得分:1)
对于类似Unix的平台,内核常数ARG_MAX
为defined 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_output
,check_call
或(很少)call
。>