命令行长度限制:内置 vs 可执行

mal*_*lat 2 shell bash limit posix

因此,根据POSIX 规范,我们有以下定义*

扩展到位置参数,从 1 开始,最初为设置的每个位置参数生成一个字段。当扩展发生在将执行字段拆分的上下文中时,任何空字段都可能会被丢弃,并且每个非空字段应进一步拆分,如字段拆分中所述。当扩展发生在不进行字段拆分的上下文中时,如果 IFS 至少包含一个字符,则初始字段应连接形成单个字段,其中每个参数的值由 IFS 变量的第一个字符分隔,或如果 IFS 未设置,则由 a 分隔,如果 IFS 设置为空字符串,则不分隔。

对于绝大多数人来说,我们都知道著名的ARG_MAX限制:

$ getconf ARG_MAX
2621440
Run Code Online (Sandbox Code Playgroud)

这可能导致:

$ cat * | sort -u > /tmp/bla.txt
-bash: /bin/cat: Argument list too long
Run Code Online (Sandbox Code Playgroud)

值得庆幸的是,背后的好人bash([包括所有类似 POSIX 的其他人])为我们提供printf了内置功能,因此我们可以简单地:

printf '%s\0' * | sort -u --files0-from=- > /tmp/bla.txt
Run Code Online (Sandbox Code Playgroud)

一切对用户都是透明的。

有人可以让我知道为什么ARG_MAX使用built-in命令绕过限制是如此微不足道,以及为什么提供一个符合标准的 POSIX shell 解释器来处理*一个独立的可执行文件的特殊参数是如此困难:

$ cat *
Run Code Online (Sandbox Code Playgroud)

那会破坏什么吗?我不是要求bash人们提供cat内置的,我只对操作顺序感兴趣,以及为什么*根据命令是内置的还是独立的可执行文件以不同的行为展开。

Kus*_*nda 12

限制不在 shell 中,而在exec()函数族中。

POSIX 标准对此表示

新进程的组合参数和环境列表可用的字节数是{ARG_MAX}。空终止符、指针和/或任何对齐字节是否包含在此总数中是实现定义的。

要运行 shell 中内置的实用程序,shell 不需要调用exec(),因此它不受此限制的影响。

还要注意,受限制的不仅仅是命令行的长度,而是命令长度、其参数以及当前环境变量及其值的组合。

还要注意,printf不是eg 中的内置实用程序pdksh(它恰好充当OpenBSD 中的shksh)。依赖它是内置的,需要考虑正在使用的特定 shell。


Ste*_*itt 6

Kusalananda回答解释了为什么ARG_MAXshell 内置函数不是问题。

至于以cat *不受 影响的方式实现ARG_MAX,这样做是微不足道的:cat实现所需要做的就是使用它glob(3)来实现自己的通配符,然后您可以使用cat \*or运行它,cat '*'以便外壳不执行其自己的通配符。您会在 Linux 或 Unix 风格的系统上找到一些命令,它们至少在某些情况下可以处理自己的通配符;find, tar,zip等等。许多具有本机 DOS 版本的命令至少会包含处理通配符的代码,因为那里的 shell 本身不通配外部命令的参数。

鉴于对 POSIX shell 的期望,该功能将相当令人惊讶且难以发现!在早期的 Unix 版本中,通配符是使用单独的程序/etc/glob.