xargs 的不规则参数计数

xargs 的不规则参数计数

为什么数字这么不规则?

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 个。

相关内容