Jes*_*ogt 14 python shell subprocess popen
我正在使用子进程模块中的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)
Run Code Online (Sandbox Code Playgroud)
我正在使用的工具会获取一个然后处理的文件列表.在某些情况下,此文件列表可能会很长.有没有办法找到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
Run Code Online (Sandbox Code Playgroud)
有没有找到这个最大长度的一般方法?我在msdn上发现了以下文章:命令提示符(Cmd.exe)命令行字符串限制但我不想在值中硬编码.我宁愿在运行时获取值以将命令分解为多个调用.
我在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]
Run Code Online (Sandbox Code Playgroud)
出现此问题的原因是paths列表中的每个实际条目通常都是一个非常长的文件路径,并且有几千个.
我不介意将命令分解为多个调用process_file.exe.我正在寻找获得args可以达到的最大长度的一般方法,因此我知道每次运行要发送多少路径.
如果您传递shell = False,则Cmd.exe不起作用.
在Windows上,子进程将使用Win32 API中的CreateProcess函数来创建新进程.此函数的文档指出第二个参数(由subprocess.list2cmdline构建)的最大长度为32,768个字符,包括Unicode终止空字符.如果lpApplicationName为NULL,则lpCommandLine的模块名称部分限制为MAX_PATH字符.
举个例子,我建议为可执行文件(args [0])提供一个值,并为第一个参数使用args.如果我读取CreateProcess文档和子进程模块源代码是正确的,这应该可以解决您的问题.
[编辑:在我的手上Windows机器并测试后删除了args [1:]位]
对于类 Unix 平台,内核常量由 POSIXARG_MAX定义。它要求至少为 4096 字节,但在现代系统上,它可能是 1 MB 或更多。
在许多系统上,getconf ARG_MAX将在 shell 提示符处显示其值。
shell 实用程序xargs允许您方便地分解长命令行。例如,如果
python myscript.py *
Run Code Online (Sandbox Code Playgroud)
在大目录中失败,因为文件列表扩展为以字节为单位的长度超过的值ARG_MAX,您可以使用类似的方法来解决它
printf '%s\0' * |
xargs -0 python myscript.py
Run Code Online (Sandbox Code Playgroud)
(该选项-0是 GNU 扩展,但实际上是唯一完全安全的方式来明确传递可能包含换行符、引用字符等的文件名列表。)也许还可以探索
find . -maxdepth 1 -type f -exec python myscript.py {} +
Run Code Online (Sandbox Code Playgroud)
这些解决限制的方法是,如果参数列表太长,它们会对其进行划分,并myscript.py在一次可以放入命令行的尽可能多的参数上运行多次。根据具体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))
Run Code Online (Sandbox Code Playgroud)
...在大多数情况下,您可能应该避免原始Popen()并让包装器功能类似run()或check_call()完成大部分工作:
r = subprocess.run(['xargs', '-0', 'command'],
input='\0'.join(long_long_argument_list),
universal_newlines=True)
out = r.stdout
Run Code Online (Sandbox Code Playgroud)
subprocess.run()text=True在 3.7+ 中支持作为universal_newlines=True. 3.5 之前的 Python 版本没有run,因此您需要回退到较旧的遗留函数check_output、check_call或(很少)call。
如果你想用xargsPython 重新实现,就像这样。
import os
def arg_max_args(args):
"""
Split up the list in `args` into a list of lists
where each list contains fewer than ARG_MAX bytes
(including room for a terminating null byte for each
entry)
"""
arg_max = os.sysconf("SC_ARG_MAX")
result = []
sublist = []
count = 0
for arg in args:
argl = len(arg) + 1
if count + argl > arg_max:
result.append(sublist)
sublist = [arg]
count = argl
else:
sublist.append(arg)
count += argl
if sublist:
result.append(sublist)
return result
Run Code Online (Sandbox Code Playgroud)
与 real 一样xargs,您将在此函数返回的每个子列表上运行单独的子进程。
如果任何一个参数大于,正确的实现应该会引发错误,ARG_MAX但这只是一个快速演示。