Bash命令行和输入限制

123

在bash(或其他shell)中,是否存在某种字符限制,规定了输入的最大长度?如果是这样的话,那么这个字符限制是多少呢?

换句话说,是否可能在bash中编写一条过长的命令而无法在命令行上执行?如果没有必要的限制,那么是否有建议的限制呢?


3
“输入限制”与“操作系统级别的参数限制”是非常不同的(需要注意的是,除了参数之外的一些内容,例如环境变量,也适用于操作系统级别的参数限制)。传递给操作系统的命令可能比生成它的 shell 命令具有更多或更少的字符。 - Charles Duffy
4个回答

163

命令行长度的限制不是由Shell强制实施的,而是由操作系统决定的。这个限制通常在几百千字节的范围内。POSIX将该限制称为ARG_MAX,在符合POSIX标准的系统上,可以使用以下命令查询它:

$ getconf ARG_MAX    # Get argument limit in bytes

例如,在Cygwin上,这个数字是32000,在我使用的不同BSD和Linux系统上,则从131072到2621440不等。

如果你需要处理一个超过此限制的文件列表,你可以考虑使用xargs实用程序,它会重复调用一个子集的参数,不超过ARG_MAX

回答你具体的问题,是的,可能会尝试运行具有过长参数列表的命令。Shell会报错并显示"argument list too long"的消息。

请注意,程序的输入(如stdin或任何其他文件描述符中读取)并没有受到限制(只受可用程序资源的限制)。因此,如果你的shell脚本读取一个字符串到变量中,你并不受ARG_MAX的限制。该限制也不适用于shell内置命令。


1
@KrzysztofJabłoński 不太可能,因为LONG_VAR的内容是通过标准输入(stdin)传递的,这完全是在shell中完成的;它不会作为cmd的参数进行扩展,因此fork()/exec()的ARG_MAX限制并不适用。你可以自己尝试一下:创建一个内容超过ARG_MAX的变量,并运行你的命令。 - Jens
2
这是澄清记录:对于一个8兆字节的m4a文件,我执行了blah="$(cat /home/schwager/Music/Recordings/20090420\ 131623.m4a)"; cat <<< $blah >/dev/null。请注意没有错误。 - Mike S
4
小提示。环境变量也要考虑在内。sysconf manpage
使用 ARG_MAX 很困难,因为未规定执行 exec(3) 的参数空间有多少用于用户的环境变量。
- Gerrit
3
我觉得BUGS中有一个非常重要的警告。例如,在macOS 10.12.6上,xargs将尝试将多少内容放入一个exec()中限制为 ARG_MAX - 4096。因此,使用xargs的脚本可能会工作,直到有一天某个人在环境中放入了过多的东西。 现在遇到这个问题(可以通过xargs -s ???绕过它)。 - neuralmer
2
@Jens,你的回答谈到了fork()/exec()的限制,而不是shell在输入行(交互式或非交互式)上可以处理多少内容。因此,这并没有回答问题。(我确实看到一些命令向shell调用其他程序,因此参数会传递到那里,但这是另一回事。) - Robert Siemer
显示剩余8条评论

52

好的,居民们。我长期以来一直认为命令行长度限制是不可改变的事实。那么对于这种假设该怎么处理呢?当然是要验证。

我有一台Fedora 22机器(即:带有bash4的Linux)。我创建了一个目录,并在其中放置了500,000个文件,每个文件名18个字符长。命令行长度为9,500,000个字符。创建方式如下:

seq 1 500000 | while read digit; do
    touch $(printf "abigfilename%06d\n" $digit);
done

并且我们注意到:

$ getconf ARG_MAX
2097152

然而请注意,我可以做到这一点:

$ echo * > /dev/null

但是这种方法失败了:

$ /bin/echo * > /dev/null
bash: /bin/echo: Argument list too long

我可以运行一个for循环:

$ for f in *; do :; done

这是另一个shell内置命令。

仔细阅读关于ARG_MAX文档的说明,它说:exec函数参数的最大长度。这意味着:如果不调用exec,就没有ARG_MAX限制。这就解释了为什么Shell内置命令没有被ARG_MAX限制。

实际上,我可以列出我的目录,如果我的参数列表长达109948个文件,或者大约2,089,000个字符(多或少)。但是,一旦我再添加一个18个字符的文件名,我就会收到一个参数列表过长的错误。所以ARG_MAX正在按照广告宣传的方式工作:exec在参数列表中包含超过ARG_MAX个字符(包括环境数据)时失败。


嗯,我没有读到现有答案暗示内置函数受到所讨论的限制,但我确实能理解为什么有人会这样认为。 - Charles Duffy
6
是的,我认为对于新手命令行爱好者来说,记住使用 bash 内建命令和 fork/exec 执行命令的情况不同,在一些不明显的方面可能会有困难。我想要澄清这一点。在作为 Linux 系统管理员进行工作面试时,我总是会被问到一个问题:"我有一个目录中的一堆文件。如何循环遍历它们..." 询问者通常都是在考虑行长限制,想寻找 find/while 或 xargs 的解决方案。在将来,我会建议他们使用 for 循环,因为它可以处理这个问题! :-) - Mike S
@MikeS 虽然你可以使用 for 循环,但如果你能使用 find-xargs 组合,你将分叉更少,速度更快。;-) - Lester Cheung
4
“@LesterCheung for f in *; do echo $f; done不会分叉(所有内置)。因此,我不知道一个find-xargs组合是否会更快;它还没有经过测试。实际上,我不知道原帖的问题集是什么。也许 find /path/to/directory 对他没有用,因为它会返回文件的路径名。也许他喜欢 for f in * 循环的简单性。无论如何,这次对话是关于行输入限制-而不是效率。因此,让我们保持主题,涉及到命令行长度。” - Mike S
就我所知,问题在于尝试用C语言编写一个shell,并确定允许输入的长度。顺便说一句。 - Derek Halden
显示剩余2条评论

2

有一个大约为1024的缓冲限制。读取将在粘贴或输入过程中中断。要解决这个问题,请使用-e选项。

http://linuxcommand.org/lc3_man_pages/readh.html

-e选项使用Readline在交互式shell中获取行。

将您的read更改为read -e,烦人的行输入中断就消失了。


2
这不是关于“读取”的问题:“也就是说,在bash中是否可能编写一个命令,其长度过长而无法在命令行上执行?” - Chai T. Rex
1
@ChaiT.Rex 你说的有点对,但是这里有个问题:尝试在没有Readline的情况下交互式运行Bash,即bash --noediting,并在新提示符下尝试运行命令echo somereallylongword,其中somereallylongword的长度超过4090个字符。在Ubuntu 18.04上尝试后,该单词被截断了,所以显然与未启用Readline有关。 - Amir
@Amir 有趣!你是正确的! 我试图编辑答案,但我意识到在这种情况下-e选项不适用于bash(在bash中,它会立即在错误时退出shell)。而且我不确定为什么Paul转向了read。无论如何,在使用--noreadline启动bash时,存在4-5000个字符的缓冲限制。那是我不知道或预料到的副作用。 - Mike S
点赞,因为读取是我在房间里追逐的问题儿童,而这个答案让我找到了正确的位置。谢谢! - keen

0
在过去,tcsh 每个命令行的字符限制为1024个,如果你有一个非常长的 $PATH,那么这将变得很困难。我被迫重新构建了一个私有版本的 tcsh,增加了缓冲区大小,以允许用户拥有长的 $PATH 设置。那是20年前的事情。那时候我放弃了使用 tcsh,并转而使用没有这种限制的 zsh。现在我只使用普通的 bash,因为它已经足够好了。

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