我编写了一个小 bash 脚本来查看当我继续跟踪指向同一目录的符号链接时会发生什么。我期待它要么创建一个很长的工作目录,要么崩溃。但结果让我大吃一惊...
mkdir a
cd a
ln -s ./. a
for i in `seq 1 1000`
do
cd a
pwd
done
Run Code Online (Sandbox Code Playgroud)
一些输出是
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a
${HOME}/a/a
${HOME}/a/a/a
${HOME}/a/a/a/a
${HOME}/a/a/a/a/a
${HOME}/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?
Sté*_*las 98
帕特里斯在他的回答中确定了问题的根源,但如果您想知道如何从那里到为什么会得到这个问题,这是一个很长的故事。
进程的当前工作目录并不复杂。它是进程的一个属性,它是目录类型文件的句柄,相对路径(在进程进行的系统调用中)从该目录开始。解析相对路径时,内核不需要知道(a)当前目录的完整路径,它只是读取该目录文件中的目录条目以找到相对路径的第一个组件(..就像任何其他文件)并从那里继续。
现在,作为用户,您有时想知道该目录在目录树中的位置。对于大多数 Unices,目录树是一棵树,没有循环。也就是说,从树的根 ( /) 到任何给定文件只有一条路径。该路径通常称为规范路径。
为了得到当前工作目录,什么程序需要做的仅仅是步行的路径(也下来,如果你想看到它的根树的底部)树回到根,找到节点的名称在途中。
例如,试图找出其当前目录是一个过程/a/b/c,将打开..目录(相对路径,所以..在当前目录中的条目),查找类型目录的文件具有相同的inode号.,找出c匹配,然后打开../..,依此类推,直到找到/。那里没有歧义。
这就是getwd()或getcwd()C 函数所做的或至少曾经做过的。
在现代 Linux 等某些系统上,有一个系统调用返回当前目录的规范路径,该目录在内核空间中进行查找(并允许您找到当前目录,即使您没有对其所有组件的读取访问权限) ,这就是getcwd()所谓的。在现代 Linux 上,您还可以通过 .readlink() 找到当前目录的路径/proc/self/cwd。
这就是大多数语言和早期 shell 在返回当前目录的路径时所做的。
你的情况,你可以打电话cd a,只要你想在5月的时间,因为它是一个符号链接.,当前目录不那么所有的改变getcwd(),pwd -P,python -c 'import os; print os.getcwd()',perl -MPOSIX -le 'print getcwd'将回报您${HOME}。
现在,符号链接使这一切变得复杂。
symlinks允许在目录树中跳转。在 中/a/b/c,如果/a或/a/b或/a/b/c是符号链接,则 的规范路径/a/b/c将完全不同。特别是,..入口/a/b/c不一定是/a/b。
在 Bourne shell 中,如果您执行以下操作:
cd /a/b/c
cd ..
Run Code Online (Sandbox Code Playgroud)
甚至:
cd /a/b/c/..
Run Code Online (Sandbox Code Playgroud)
不能保证你最终会在/a/b.
就像:
vi /a/b/c/../d
Run Code Online (Sandbox Code Playgroud)
不一定等同于:
vi /a/b/d
Run Code Online (Sandbox Code Playgroud)
ksh引入了一个逻辑当前工作目录的概念来解决这个问题。人们习惯了它,POSIX 最终指定了这种行为,这意味着现在大多数 shell 也这样做:
对于cd和pwd内置命令(并且仅适用于它们(尽管也适用于具有它们的外壳上的popd/ pushd)),外壳保持其对当前工作目录的自己的想法。它存储在$PWD特殊变量中。
当你这样做时:
cd c/d
Run Code Online (Sandbox Code Playgroud)
即使c或c/d是符号链接,而$PWDcontains /a/b,它也会附加c/d到末尾,因此$PWD变为/a/b/c/d。当你这样做时:
cd ../e
Run Code Online (Sandbox Code Playgroud)
而不是做chdir("../e"),它确实chdir("/a/b/c/e")。
并且该pwd命令只返回$PWD变量的内容。
这在交互式 shell 中很有用,因为pwd输出当前目录的路径,该路径提供有关您如何到达那里的信息,并且只要您只..在参数中使用cd而不是其他命令,就不太可能让您感到惊讶,因为cd a; cd ..或cd a/..通常会让您回来到你所在的地方。
现在,$PWD除非您执行cd. 直到下次调用cdor 时pwd,可能会发生很多事情, 的任何组件$PWD都可以重命名。当前目录永远不会改变(它总是相同的 inode,尽管它可以被删除),但它在目录树中的路径可能会完全改变。getcwd()每次调用时都会通过遍历目录树来计算当前目录,因此它的信息总是准确的,但是对于 POSIX shell 实现的逻辑目录,中的信息$PWD可能会变得陈旧。所以在运行cdor 时pwd,一些 shell 可能想要防止这种情况发生。
在那个特定实例中,您会看到不同 shell 的不同行为。
有些人喜欢ksh93完全忽略问题,因此即使在您调用之后也会返回不正确的信息cd(并且您不会看到您在bash那里看到的行为)。
有些人喜欢bash或zsh确实检查它$PWD仍然是当前目录的路径 on cd,但不是 on pwd。
pdksh 确实检查pwd和cd(但在pwd,不更新$PWD)
ash(至少在 Debian 上找到的那个)不检查,当您检查时cd a,它实际上会检查cd "$PWD/a",因此如果当前目录已更改并且$PWD不再指向当前目录,它实际上不会更改为当前a目录中的目录,但其中的一个$PWD(如果不存在则返回错误)。
如果你想玩它,你可以这样做:
cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)
Run Code Online (Sandbox Code Playgroud)
在各种贝壳中。
在您的情况下,由于您使用的是bash, 在 a 之后cd a,bash检查$PWD仍指向当前目录。为此,它调用stat()的值$PWD来检查其 inode 编号并将其与的值进行比较.。
但是当查找$PWD路径涉及解析太多符号链接时,会stat()返回一个错误,因此 shell 无法检查是否$PWD仍然对应于当前目录,因此它会再次计算它getcwd()并相应地更新$PWD。
现在,为了澄清 Patrice 的答案,查找路径时遇到的符号链接数量的检查是为了防止符号链接循环。最简单的循环可以用
rm -f a b
ln -s a b
ln -s b a
Run Code Online (Sandbox Code Playgroud)
如果没有那个安全保护,在 a 上cd a/x,系统将不得不找到a链接到的位置,找到它b是链接到 的符号链接a,这将无限期地持续下去。防止这种情况的最简单方法是在解决超过任意数量的符号链接后放弃。
现在回到逻辑当前工作目录以及为什么它不是那么好的功能。重要的是要意识到它仅适用cd于 shell 而不是其他命令。
例如:
cd -- "$dir" && vi -- "$file"
Run Code Online (Sandbox Code Playgroud)
并不总是相同的:
vi -- "$dir/$file"
Run Code Online (Sandbox Code Playgroud)
这就是为什么您有时会发现人们建议始终cd -P在脚本中使用以避免混淆(您不希望您的软件处理../x与其他命令不同的参数,只是因为它是用 shell 而不是另一种语言编写的)。
该-P选项是禁用的逻辑目录处理,以便cd -P -- "$var"实际执行调用chdir()上的内容$var(至少只要$CDPATH它不设定,除非$var是-(或可能-2,+3......在一些炮弹),但这是另一回事)。在 a 之后cd -P,$PWD将包含一个规范路径。
Pat*_*que 45
这是 Linux 内核源代码中硬编码限制的结果;为防止拒绝服务,嵌套符号链接的数量限制为 40(在内部follow_link()函数中找到,在内核源代码中fs/namei.c调用nested_symlink())。
对于支持符号链接的其他内核,您可能会得到类似的行为(并且可能是 40 以外的另一个限制)。
| 归档时间: |
|
| 查看次数: |
9210 次 |
| 最近记录: |