为什么数字这么不规则?
echo {1..200000} | xargs perl -E 'say "ok:", scalar @ARGV'
ok:23691
ok:21840
ok:21840
ok:21840
ok:20261
ok:18720
ok:18720
ok:18720
ok:18720
ok:15648
标准论证长度更文明。
perl -E' say "1 " x 900000' | xargs perl -E 'say "ok:", scalar @ARGV'
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:65520
ok:48240
到底什么是关键因素?
答案1
重要的数字是(所有)参数的总长度,以及 xargs 决定使用的命令缓冲区大小。
第一个取决于您运行的命令的固定命令行,并且参数 xargs 给出每个调用。perl -E 'say "ok:", scalar @ARGV'
是 32 个字节,计算终止字符串的 NUL 字节(即perl<NUL>-E<NUL>say "ok:", scalar @ARGV<NUL>
。在第二个示例中,所有参数每个都是两个字节,1<NUL>
.所以 32 + 65520 * 2 字节,或 131072 B = 128 * 1024 B = 128 kB。
显然,在第一个示例中,参数的长度不同,给出的计数也不同,但逻辑应该是相同的。例如,第二次到第四次运行的 21840 args 匹配 5 位参数(每个参数 6 个字节):21840 * 6 + 32 = 131072。
命令缓冲区的大小可能取决于实现,但 GNU xargs 可以用 来显示它xargs --show-limits
,并且在我的 Linux 上,我得到:
$ echo | xargs --show-limits
Your environment variables take up 2305 bytes
POSIX upper limit on argument length (this system): 2092799
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 2090494
Size of command buffer we are actually using: 131072
Maximum parallelism (--max-procs must be no greater): 2147483647
查看倒数第二行,这是完全相同的数字。
您可以使用 更改它使用的缓冲区大小-s
,例如仅使用 10 kB 缓冲区:
$ perl -E' say "1 " x 90000' | xargs -s 10240 perl -E 'say "ok:", scalar @ARGV'
ok:5104
ok:5104
ok:5104
...
当然,还需要-n
限制单个参数的数量:
$ echo {1..200000} | xargs -n 10000 perl -E 'say "ok:", scalar @ARGV'
ok:10000
ok:10000
ok:10000
...
--show-limits
提到环境变量是因为它们使用与命令行参数相同的空间,并且如果您将缓冲区大小提高到足够接近系统最大值,它们的大小也开始重要。
我不确定系统是否也计算了指针到参数字符串反对限制,但至少 xargs 似乎并不关心这一点。
答案2
对于后台上下文:Linux 和其他 Unice 对可传递给命令的参数的最大数量有限制,在某些情况下对结果命令行的总长度也有限制。xargs
需要处理这个问题,否则当它执行带有大量参数的命令时(就像在本例中一样),它就会有时会E2BIG
失败execvp
。
许多在线资源会告诉您这ARG_MAX
是参数的规范限制,但它们是错误的:ARG_MAX
是针对单个参数的,即使如此,在实践中可能还有更多的限制在此之前生效。这些限制通常根据数量来运作字节在结果命令行中,而不是数量论点(即基于指针数组)。在本例中,您要了解这样一个事实:在 xargs 的 GNU 版本中,分割命令的点是在用户空间中根据固定长度 ( bc_args_exceed_testing_limit
) 确定的。xargs --show-limits
如果您使用 GNU xargs,您可以看到其中一些限制:
% xargs --show-limits </dev/null |& grep -e arg -e command
POSIX upper limit on argument length (this system): 2090868
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 2086632
Size of command buffer we are actually using: 131072
如果您希望在源代码中看到这些限制,在 中xargs/xargs.c
,我们可以看到我们关闭了子项,然后在其中fork()
之前对参数长度进行测试:exec()
[...]
/* If we run out of processes, wait for a child to return and
try again. */
while ((child = fork ()) < 0 && errno == EAGAIN && procs_executing)
wait_for_proc (false, 1u);
switch (child)
{
case -1:
die (EXIT_FAILURE, errno, _("cannot fork"));
case 0: /* Child. */
{
close (fd[0]);
child_error = EXIT_SUCCESS;
prep_child_for_exec ();
if (bc_args_exceed_testing_limit (argv))
errno = E2BIG;
else
execvp (argv[0], argv);
[...]
这张支票来自lib/buildcmd.c
:
/* Return nonzero if the indicated argument list exceeds a testing limit.
* NOTE: argv could be declared 'const char *const *argv', but it works as
* expected only with C++ compilers <http://c-faq.com/ansi/constmismatch.html>.
*/
bool
bc_args_exceed_testing_limit (char **argv)
{
size_t chars, args;
for (chars=args=0; *argv; ++argv)
{
++args;
chars += strlen(*argv);
}
return (exceeds ("__GNU_FINDUTILS_EXEC_ARG_COUNT_LIMIT", args) ||
exceeds ("__GNU_FINDUTILS_EXEC_ARG_LENGTH_LIMIT", chars));
}
由于后面的数字以字符串形式更长,因此它们占用更多空间,从而导致 xargs 更急切地生成新命令。
答案3
xargs
需要将参数调整为设定的大小这受到操作系统的限制 - 第一个示例的参数会增长,因此每次调用时不太适合perl
。您可以在限制中容纳比 5 字符参数更多的 3 字符参数。
第二个总是“1”作为参数,因此每次都适合 65520 个。