我正在尝试使用以下脚本创建一棵大目录树:
for i in {1..5000}
do
mkdir $i
cd $i
done
Run Code Online (Sandbox Code Playgroud)
但脚本在创建 1038 到 1040 个目录后停止。linux系统有什么限制吗?
Linux 对文件路径的长度没有真正的限制。(大多数 Linux 文件系统确实限制了文件名的长度,通常限制为 256 个字符。但对路径中的组件数量没有限制。)所以您的脚本可能有效。
但是,bash(和其他 shell)确实存在长路径名的问题。特别是bash交互式 shell中的错误意味着一旦您在目录层次结构中如此深以至于当前工作目录的路径名的长度超过了某个限制,您就不会在终端会话中看到正确的路径信息。这可能非常令人困惑;它可能会让您相信脚本失败了。
这是一个示例终端会话。我首先更改我的提示以避免用当前路径填满整个屏幕:
~/tmp$ export PS1='\W\$ '
tmp$ for i in {1..5000}; do mkdir $i; cd $i; done
10$ # The prompt is incorrect!
10$ basename "$PWD"
5000
10$ cd ~/tmp
tmp$ # count the number of files in the hierarchy
tmp$ find 1 -type v | wc -l
5000
Run Code Online (Sandbox Code Playgroud)
bash也展示了一个不相关的问题,即非常深的文件层次结构会导致某些操作(例如cd)非常缓慢,这大概是因为 shell 选择从根开始验证整个路径。在 bash 的情况下,这会导致stat对路径的每个组件的调用,因此mkdir/cd脚本中的循环在文件树的深度上变为二次的。zsh,否则似乎可以正确处理长路径名,当移动到路径名包含很多组件的目录时,速度也会大大减慢,尽管不如bash多(也不经常)
bash —内置的bash cd可以工作,但如上所述,当有很多组件时,它会非常慢。Bash的内置pwd函数似乎工作正常,但 中的替换逻辑却不能这样说$PS1,它用于在交互式 shell 中生成提示。很长的提示也会混淆readline库,因此如果您的默认$PS1包含\w(工作目录的完整路径),您会发现您的 bash 会话基本上无法使用。如上面的终端会话所示,您可以更改要使用的提示\W(工作目录的基本名称),但由于计算它的路径名已经被截断,因此会产生一个非常具有误导性的提示。
dash —内置的dash cd无法处理长路径名。当路径名超过此限制时失败。这会导致您的脚本生成一长串错误消息,每个 failed 一个cd,所以我假设您没有使用dash。
奇怪的是,内置的破折号 pwd可以在长路径名上正常工作,因此如果您在路径名很长的目录中开始破折号,您将能够看到正确的路径名,即使您将无法使用cd导航到其他文件夹。
ksh —cd内置函数运行迅速,但其参数长度有限。此外,pwd内置程序有问题,会在一定限度内截断路径。$PWD也被截断,因此ksh如果您请求包含当前工作目录的提示,提示将是错误的。但是,有一个解决方法:pwd大多数 Linux 发行版中包含的命令行实用程序(通常在/bin/pwd)确实可以正确处理长路径名,因此您可以使用它env pwd来查看正确的路径:
PS1='$(basename "$(env pwd)") $ '
Run Code Online (Sandbox Code Playgroud)
zsh — 我在zsh 中发现的唯一问题是cd当路径有很多组件时需要花费大量的时间。如果您的默认提示包含完整路径名,您的交互式 shell 将没有太多空间用于其他任何内容,但似乎没有其他问题。如果您使用限制要查看的路径名组件的数量,您将得到一个可用的提示。
可能,我刚才提到的所有错误都有相同的来源。
传统上,Unix 系统库定义了一个名为 的宏PATH_MAX,它可用于创建一个足够大的缓冲区以容纳任何有效路径。假设是路径名存在一些系统限制,并且该限制不会太大。典型值(取决于操作系统)范围从 256 到 4096。
现代 Posix 标准没有对实际最大路径长度做出任何声明,但它仍然定义了PATH_MAX宏:
PATH_MAX
- 实现将作为路径名存储在用户提供的未指定大小的缓冲区中的最大字节数,包括终止空字符。实现将接受的最小数字作为路径名中的最大字节数。
在 Linux 上,PATH_MAX是 4096。请注意,虽然没有什么可以阻止您PATH_MAX在代码中重新定义,但它不会产生任何影响,因为该数字已经有效地编译到内核中。
换句话说,如果您使用某个系统接口将路径名存储到缓冲区中,并且该接口没有任何方法来提供缓冲区的长度,则可以假设将不超过PATH_MAX字节存储到缓冲区中. 但这并不限制路径的长度。这只是意味着您用来确定路径的接口将返回一个截断的值。
此类接口的一个示例是getwd,它将当前工作目录的完整路径存储到缓冲区中。getwd不允许您指定缓冲区的大小,因此路径名PATH_MAX - 1在 Linux 上被截断为或 4095 字节。
该接口很久以前就被替换为getcwd,它将缓冲区及其长度作为参数。但是 Posix 实现getcwd没有提供任何机制来确定缓冲区需要多大,所以PATH_MAX无论如何使用常量是很常见的。然而,Posix 确实建议实现可以允许调用者提供NULL而不是缓冲区,在这种情况下,getcwd将自动分配一个足够大的缓冲区,填充它并返回它。(调用者随后必须调用free()以返回分配的内存。)Linux 实现getcwd正是这样做的,它允许程序(例如 shell)获得完整的未截断路径名。显然,bash确实在其内置的pwd,以及创建$PWDshell 变量。但是$PS1提示替换器没有;创建提示时,路径被截断为PATH_MAX - 1字符。并且该截断发生在路径名减少为其基本名称之前\W,因此如果您有一个很长的路径并且\W在您的 中$PS1,您将从路径的中间获得组件的一部分。
所有这些都使得查看您的脚本是否有效变得非常混乱。但是在我尝试过的所有 shell 上,除了dash,文件夹都被正确创建(使用bash和zsh非常慢)。