Python的'sys.argv'在传递参数时有数量限制吗?

3

我有一个需要处理大量文件的Python脚本。为了绕过Linux限制传递给命令的参数数量相对较小的问题,我使用了find -print0xargs -0

我知道另一种选择是使用Python的glob模块,但当我需要进行更高级的find命令时,这并没有什么帮助,因为它需要查找修改时间等信息。

当我在大量文件上运行我的脚本时,Python只接受其中的一部分参数,一开始我以为是在argparse中出现了限制,但似乎是在sys.argv中。我找不到任何关于这个问题的文档。这是一个bug吗?

下面是一个示例Python脚本,说明了这一点:

import argparse
import sys
import os

parser = argparse.ArgumentParser()
parser.add_argument('input_files', nargs='+')
args = parser.parse_args(sys.argv[1:])

print 'pid:', os.getpid(), 'argv files', len(sys.argv[1:]), 'argparse files:', len(args.input_files)

我有很多文件需要在此上运行:

$ find ~/ -name "*" -print0 | xargs -0 ls > filelist
748709 filelist

但看起来xargs或Python正在将我的大文件列表分块,并使用多个不同的Python运行处理它:

$ find ~/ -name "*" -print0 | xargs -0 python test.py
pid: 4216 argv files 1819 number of files: 1819
pid: 4217 argv files 1845 number of files: 1845
pid: 4218 argv files 1845 number of files: 1845
pid: 4219 argv files 1845 number of files: 1845
pid: 4220 argv files 1845 number of files: 1845
pid: 4221 argv files 1845 number of files: 1845
...

为什么要创建多个进程来处理列表?为什么需要切块?我认为文件名中没有换行符,-print0-0不应该会有问题吧?如果有换行符,我期望以上示例的sed -n '1810,1830p' filelist会显示一些奇怪的东西。这是什么原因?
我差点忘了:
$ python -V
Python 2.7.2+

奇怪的问题。当然,作为另一种选择,你也可以在脚本内部解析filelist - Benjamin Bannier
1
这就是xargs的作用。它仍然需要通过shell调用Python,因此在参数上会有相同的限制。为什么不直接让你的Python程序接收~/-name *参数呢? - John La Rooy
1
我曾经认为xargs以某种神奇的方式解决了有限参数空间的问题。结果发现它只是将较小的块分叉成单独的进程。而且事实证明,在我使用xargs的每个应用程序中,这种行为都没有任何区别,除了这一个... - Jake Biesinger
5个回答

7

xargs 默认会将参数进行分块。请查看 xargs--max-args--max-chars 选项。它的手册页也解释了限制(在 --max-chars 下)。


谢谢。我之前没有看到这个。你有任何想法为什么上面的命令find ~/ -name "*" -print0 | xargs -0 ls > filelist实际上能够工作吗?看起来ls会被多次调用,都写入(而不是追加!)到同一个文件中。也许文件只打开一次,我们捕获了xargs的输出? - Jake Biesinger
Shell会处理重定向。ls实际上输出到stdout。可以将其视为括号内的行和括号外的重定向。 - Lars Kotthoff

2

Python似乎没有对参数数量设置限制,但操作系统有。

请在这里查看更全面的讨论。


2

你从find中需要的所有内容都可以从os.walk中获取。

不要使用find和shell来完成这些任务。

使用os.walk,并在Python中编写所有规则和过滤器。

"查找修改时间"意味着你将会使用os.stat或某个类似的库函数。


1
我基本上同意从 Python 内部完成所有这些工作的方式,使用 os.walk、glob.glob 和 os.stat。但我不知道的是,xargs 仍然遵循操作系统限制,并只是使用剩余参数多次调用命令。 - Jake Biesinger
“原则上同意”意味着“不同意”。但是,你没有提供理由。以下是为什么你的决定都不可取的原因:所有的 Python 更快、更简单、更灵活。更简单是因为它只是一个语言:Python。更快是因为它全部在单个进程中运行(无需交换)。如果想要更快的速度,请使用 multiprocessing。最后,它更灵活,因为你不受 find 奇怪限制的约束。简化应用程序没有任何缺点。 - S.Lott
应用程序通常不需要处理数千个文件。工作通常在几十个文件(染色体)上完成;在我正在测试的特定基准测试中,我有数千个文件。也许我的原始帖子没有表达清楚。 - Jake Biesinger
@JakeBiesinger:也许我的评论没有明确表明,如果您正在处理一个文件或数百万个文件,则“find”仍然是一个不好的选择。与其重复原因,我会总结说,用Python替换基于“find”的shell脚本没有任何缺点。没有缺点。有很多优势。 - S.Lott
当然,这样做也有缺点,会在脚本中增加额外的逻辑。现在,除了对文件执行任何操作之外,还必须处理如何遍历文件系统。find非常强大,已经能够出色地完成这项工作。 - CervEd

1

xargs会尽可能地传递参数,但仍有限制。例如,

find ~/ -name "*" -print0 | xargs -0 wc -l | grep total

这将会给您多行输出。

您可能希望让脚本接受一个包含文件名列表的文件,或者从标准输入接受文件名。


0
问题在于xargs受调用参数的字符数限制(最大2091281)。
快速测试表明,这取决于路径长度,范围从5000个文件到55000个文件不等。
获取更多文件的解决方案是通过标准输入接受文件路径的管道传输。 find ... -print0 | script.py
#!/usr/bin/env python3

import sys

files = sys.stdin.read().split('\0')
...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接