我有一个使用subprocess.check_output调用的第三方可执行文件,遗憾的是我的参数列表太长并且反复调用它比用多个参数调用它要慢得多。
由于多次执行命令调用而变慢:
def call_third_party_slow(third_party_path, files):
for file in files:
output = subprocess.check_output([third_party_path, "-z", file])
if "sought" in decode(output):
return False
return True
快速但有很多文件时失败:
def call_third_party_fast(third_party_path, files):
command = [third_party_path, "-z"]
command.extend(files)
output = subprocess.check_output(command)
if "sought" in decode(output):
return False
return True
有没有简单的方法可以解决命令长度限制或轻松分组文件以避免超过os依赖长度?
您可以像这样批处理文件列表:
def batch_args(args, arg_max):
current_arg_length = 0
current_list = []
for arg in args:
if current_arg_length + len(arg) + 1 > arg_max:
yield current_list
current_list = [arg]
current_arg_length = len(arg)
else:
current_list.append(arg)
current_arg_length += len(arg) + 1
if current_list:
yield current_list
所以方法体看起来像这样:
os_limit = 10
for args in batch_args(files, os_limit):
command = [third_party_path, "-z"]
command.extend(args)
output = subprocess.check_output(command)
if "sought" in decode(output):
return False
return True
我不确定的两件事:
将arg_max调整为可能的值。可能有一些方法可以找到每个操作系统。 Here是关于某些操作系统的最大args大小的一些信息。该网站还声明Windows有32k的限制。
也许使用子进程库有更好的方法,但我不确定。
此外,我没有做任何异常处理(列表中的args超过最大大小等)
我通过在Windows上使用临时文件解决了这个问题。对于Linux,命令可以按原样执行。
为不同平台构建完整命令的方法:
import tempfile
temporary_file = 0
def make_full_command(base_command, files):
command = list(base_command)
if platform.system() == "Windows":
global temporary_file
temporary_file = tempfile.NamedTemporaryFile()
posix_files = map((lambda f: f.replace(os.sep, '/')),files)
temporary_file.write(str.encode(" ".join(posix_files)))
temporary_file.flush()
command.append("@" + temporary_file.name)
else:
command.extend(files)
return command
将文件用作全局变量可确保在执行后清除它。
这样我就不必为不同的操作系统找到最大命令长度
如果您不想重新发明最佳解决方案,请使用已经实现此功能的工具:xargs
。
def call_third_party_slow(third_party_path, files):
result = subprocess.run(['xargs', '-r', '-0', third_party_path, '-z'],
stdin='\0'.join(files) + '\0', stdout=subprocess.PIPE,
check=True, universal_newlines=True)
if "sought" in result.stdout:
return False
return True
您会注意到我也切换到subprocess.run()
,它可以在Python 3.5+中使用
如果你想重新实现xargs
,你需要找到内核常量ARG_MAX
的值,并构建一个大小永远不会超过这个限制的命令行列表。然后你可以在每次迭代后检查输出是否包含sought
,如果有,则立即退出。