我在用GNU bash, version 4.3.42(1)-release (x86_64-redhat-linux-gnu)
。
我创建了一个虚拟目录hello
并移至那里。进入后/tmp/hello
,我删除了目录本身。
$ pwd
/tmp/hello
$ rmdir $PWD
我检查了一下,我仍然在那个目录中。
$ pwd
/tmp/hello
然后我尝试移动到目录本身,当然这是不可能的(即使我已经在那里):
$ cd $PWD
bash: cd: /tmp/hello: No such file or directory
但后来我尝试了cd .
,因为它应该与 相同cd $PWD
。这有效......不知何故:
$ cd .
cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
令我惊讶的是,现在我处于一个新的“事物”中:
$ pwd
/tmp/hello/.
如果我尝试转到上一个目录,它当然会失败:
$ cd -
bash: cd: /tmp/hello: No such file or directory
有趣的是,man
那里甚至没有一个页面可以工作:
$ man cd
man: can't change directory to '': No such file or directory
man: command exited with status 255: (cd && LESS=-ix8RmPm Manual page cd(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$PM Manual page cd(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$ MAN_PN=cd(1) less -s)
为什么会发生这一切?
答案1
同意@BinaryZebra,公平地指出这个问题之前已经被问过(例如,使用命令行界面从内部删除目录[复制],并且可以被引用无限期),并且可观察到的特征是众所周知的。
关于与 POSIX 相关的行为的评论更有趣,因为它可以提供一些关于预期的行为。相关的 POSIX 文档是
在里面应用程序使用cd 部分,值得注意的是
由于 cd 影响当前 shell 执行环境,因此它始终作为 shell 常规内置命令提供。
在里面描述cd 部分第 10 项提供了最相关的信息:
- 然后 cd 实用程序应执行相当于chdir()调用函数柯帕斯作为路径参数。如果这些操作因任何原因失败,
cd
实用程序将显示相应的错误消息,并且不会执行此步骤的其余部分。如果该-P
选项无效,则PWD
环境变量应设置为 curpath 在进入步骤 9 时(即转换为相对路径名之前)的值。如果该-P
选项有效,则PWD
环境变量应设置为将输出的字符串pwd -P
。如果新目录或该目录的任何父目录没有足够的权限来确定当前工作目录,PWD
则未指定环境变量的值。
也就是说,PWD
是一个输出命令的cd
,而不是输入,因此很大程度上是被忽略通过cd
命令。请注意有关“未指定”的最终声明。 POSIX 拒绝透露$PWD
如果调用chdir()
失败会发生什么情况。
在讨论中环境变量对于pwd
,POSIX 注释如下PWD
:
当前工作目录的绝对路径名。如果应用程序设置或取消设置 的值
PWD
,则 的行为pwd
是未指定的。
也就是说,POSIX 拒绝指定$PWD
除了成功使用该cd
命令之外的任何其他可以设置的方式(反过来,匹配成功的调用chdir()
)。
在这两个文档中都没有可以解释为路径被“缓存”的措辞,而只是工作目录存在(大概是作为价值)内Shell执行环境。其本身的讨论cd
并未提及目录在没有名称的情况下继续存在的可能性。文档本身也不cd
关注不存在的目录。这是在描述中完成的chdir()
功能是导致故障的几个原因之一。例如
[ENOENT]
的一个组成部分小路不命名现有目录或小路是一个空字符串。
的描述chdir()
没有提及任何特殊处理,"."
也许是因为 POSIX 避免详细描述使用以下命令计算绝对路径名的过程getcwd()
,例如,允许没有"."
和 的文件系统".."
。如果确实如此,有人可能会添加措辞来指出实现可能被视为chdir(".")
无操作。
回到步骤 10,它明确指出:
如果这些操作因任何原因失败,cd 实用程序将显示相应的错误消息,并且不会执行此步骤的其余部分。
更新PWD
完成后那句话,所以没有改变的可能它是发生故障时的价值。
回到pwd
,它谈到了这个-L
选项
如果
PWD
环境变量包含当前目录的绝对路径名,且不包含文件名点或点-点,pwd
则应将此路径名写入标准输出。
然后
如果既没有指定
-L
,也-P
没有指定,pwd
则实用程序的行为就像-L
已指定一样。
因此,存在一系列假设行为,但没有明确声明 shell用途变量PWD
本身作为工作目录的容器。相反,有以下几点:未指定这使得一些符合 POSIX 标准的应用程序可以选择不同的数据组织方式,只要它满足“假设行为”点。
答案2
重复描述 POSIX 文档的一些个人解释对 OP 没有帮助。还有其他几个问题已经详细解释了 POSIX。然而,这个问题。
一些基本描述:
A。 Linux/Unix 的一个众所周知的特性是文件(目录)在使用时可能会被删除。这就是为什么你可以在删除电影后继续观看它。只有当目录项的inode被删除时,文件才真正消失。
乙。即使环境变量 $PWD 的值发生更改,shell 也会保留实际 pwd 的内部值。
@yaegashi 接受的答案的第一条评论显示了在哪里查看(pwd 的来源):
我是对的。看的来源
pwd_builtin()
。它只是打印 the_current_working_directory 的内容。 – yaegashi 7 月 31 日 16:01
如图所示,即使从 PWD 的奇数值开始,shell 仍然了解 pwd 的正确值(“/home/user”)。
C。应该很容易理解,在某些极端情况下,shell 保存的值可能与现实不同步。
在已删除的目录中进行操作cd .
似乎是这些极端情况之一。似乎 shell 会在失败时将点添加到最后一个已知的 pwd 中。
你的问题:
1。删除 PWD 的目录并执行内置 pwd。
$ pwd
/tmp/hello
$ rmdir $PWD; pwd
/tmp/hello
内置 pwd 报告 shell 在内存中保留的 pwd 值。
外部/bin/pwd
将报告失败。
有趣的是,报告pwd -P
(即使是 Bash 的内置)将报告失败:
$ pwd -P
pwd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
并且,使用内置命令后,pwd -P
正常的 pwd 也会失败。我相信是因为内存中 pwd 的值被更新了。
2.cd $PWD
然后我尝试移动到目录本身,当然这是不可能的(即使我已经在那里):
$ cd $PWD bash: cd: /tmp/hello: No such file or directory
任何沿着目录路径的真正尝试都将在无法读取目录时失败(当目录不存在时)。
3.光盘 。
但后来我尝试使用
cd .
,因为它应该与 cd $PWD 相同。这有效......不知何故:$ cd . cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
shell 尝试访问它知道的 pwd 路径,但失败了。这就是 shell 报告的内容:失败!这里没问题。
4.新路径。
令我惊讶的是,现在我处于一个新的“事物”中:
$ pwd /tmp/hello/.
PWD 内存中的值与实际情况不同步,并且添加了一个点 (.)。因为任何重复使用cd .
都会添加一个点。
$ cd .; pwd
/tmp/hello/./.
而且,出乎意料的是, acd ..
只会生成 pwd 和 $PWD ..
。
$ cd ..; pwd; echo $PWD
..
..
$ cd ..; pwd; echo $PWD
../..
../..
5.以前的 PWD。
如果我尝试转到上一个目录,它当然会失败:
$ cd - bash: cd: /tmp/hello: No such file or directory
之所以如此,是因为无法遵循 PWD 的先前值(存储在 OLDPWD 中的 bash 中)。
这是(正如您上面所报告的)/tmp/hello/.
,(被你好删除)无法遵循,并且成功cd -
或cd $OLDPWD
失败。
$ echo $OLDPWD # at this point following your exact procedure.
/tmp/hello/.
6.手册页
有趣的是,甚至连手册页都不起作用:
此时,手册页对我来说很有效,事实上,在所有方面都是如此。
答案3
POSIX要求缓存当前目录路径,并且删除的目录仍然存在但没有名称。
cd .
显然只是将该字符串添加到当前缓存的名称中。