在这样的命令中:
find /data ! -type d -exec rm -f {} +
用于+
批量执行rm -f
.find
应该批处理尽可能多的参数。但它怎么知道极限呢?
答案1
当调用withfind
指定的命令时,对批处理参数的能力的限制通常由内核确定:它是赋予函数系列的参数的最大大小。 POSIX 定义了两种方法来发现与此相关的值,即参数的最大大小-exec
+
exec
和环境给予一个exec
电话。
第一个是常量,因此在构建可执行文件时最终会“嵌入”可执行文件;它是常数ARG_MAX
在limits.h
:
参数的最大长度执行功能包括环境数据。
其中第二个在运行时可用:它涉及使用功能sysconf
,特别是_SC_ARG_MAX
论证。
设置的限制ARG_MAX
(适用于上述两种方法,因为它们都提供对“{ARG_MAX} 变量”的访问)是由 POSIX 指定, 关于-exec
:
任何两个或多个路径名集合的大小均应受到限制,以便实用程序的执行不会导致超出系统的 {ARG_MAX} 限制。
同样的道理xargs
:
这参数实用程序应限制命令行长度,以便在调用命令行时,组合的参数和环境列表(请参阅执行POSIX.1-2017 系统接口卷中的函数系列)不得超过 {ARG_MAX}-2048 字节。
各种实现以各种方式应用这些限制,有时应用比上述常量指示的值更小的值。例如,OpenBSDfind
通过检查sysconf
来确定最大命令行长度,同时也任意限制数字参数数量达到 5000;看源代码详细信息(感谢莫斯维供参考)。 GNUfind
检查sysconf
,并在必要时回退到ARG_MAX
,或find
指定的限制;此外,它还添加了指定的 2048 字节余量xargs
(GNUfind
并xargs
在此处分享其实现)。
特定的内核也可以添加自己的变化。什么定义了命令单个参数的最大大小?针对 Linux 讨论了这一点。由于不同的堆栈要求,Solaris 显然需要考虑不同的限制,具体取决于生成的进程(不是find
或xargs
进程,而是未来的子进程)是 32 位还是 64 位;看libfind
详细信息(感谢狡猾的对于指针)。赫德根本不限制参数。
答案2
我最近在这里提到了一般规则:
这条规则的有效实施是我自己的libfind
:https://sourceforge.net/p/schillix-on/schillix-on/ci/default/tree/usr/src/lib/libfind/find.c#l2020
这里的主要问题是libfind
需要知道当前环境大小以及正在调用的程序是 32 位程序还是 64 位程序,因为有不同的限制......
libfind
之所以区分 32/64 位,是因为以前,当我在调用 32 位程序时从 64 位 shell 调用find -name '*.c' -exec count -t {} +
获取较大项目的源代码行数时,我经常会遇到限制。libfind
count
Solarisfind
实现不需要进行这种区分,因为 Solaris 不提供 64 位查找,因此使用 32 位限制至少在任何情况下都可以工作 - 即使它不使用最大可能的参数列表大小。
顺便说一句:因为find
Linux 上的单个参数 (128k) 不太可能适用不必要的额外限制。这make
是一个真正的问题,因为整个 shell 命令行都是作为单个参数传递的。另一方面,make
不提前检查,因为make
不包含分割长命令的代码。
PS:我刚刚发现了 Solaris 上的一个有趣的限制:两者都通过 from 调用它们的程序,并且如果xargs
要调用的程序是不带 srcipt 的脚本,则实现会调用脚本的 shell 并使用固定的参数重新排序参数。大小数组。由于该数组只有 255 个条目,因此将 和的参数限制为 255,以防命令是一个如此简单的 shell 脚本。如果程序是这样的脚本并且 arglist 包含超过 255 个参数,则将返回.find
execvp()
libc
#!
execvp()
xargs
find
execvp()
E2BIG
这里的问题是:您不能使用malloc()
inside,execvp()
因为execvp()
可能是从通过 . 创建的进程中调用的vfork()
。如果execvp()
调用malloc()
,这将导致父级中分配的内存无效...alloca()
另一侧的调用总是成功,但可能会导致SIGSEGV
超出本地堆栈大小的情况。