子进程参数列表为长



我有一个第三方可执行文件是使用 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

有什么简单的方法可以绕过命令长度限制或轻松对文件进行分组以避免超过操作系统相关长度?

您可以像这样批处理文件列表:

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

有两件事我不确定:

  1. 可执行文件本身的路径是否计入限制?如果是 ->将其添加到每批的限制中。(或将arg_max减少 exe 字符串的长度(
  2. 参数之间的空间算不算?如果没有,则删除两个 +1 出现。

尽可能调整arg_max。可能有一些方法可以为每个操作系统找到这一点。以下是有关某些操作系统的最大参数大小的一些信息。该网站还声明Windows有32k的限制。

也许有更好的方法来使用子进程库来做到这一点,但我不确定。

此外,我没有进行任何异常处理(列表中超过最大大小的参数等(

我通过使用 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=''.join(files) + '', 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,如果包含,请立即退出。

最新更新