首页 > 解决方案 > 为什么当我的 shell 使用该目录时可以删除该目录?

问题描述

我不知道我遇到的是错误还是预期的行为,但幸运的是我很快就弄清楚了发生了什么。

我将我的 shell cd'd 放在 git repo 的一个子目录中,然后我执行了一个git rebase -isquash 操作,其中涉及的提交包括该目录的创建。

在该操作顺利完成后,shell 处于孤立状态,其中git status(在 zsh 主题帮助程序 RPROMPT 和运行时都没有)表明我什至不在 git 存储库中。

一旦我跑了cd ..一切都很好,这一切都是有道理的。在变基操作过程中,Git 已经rm'd 我所在的目录(在变基的第一步)并随后将其放回原处。既然放回去了,我也可以cd $(pwd)一步步跑过去。

这是一些令人困惑的行为。虽然,尚不清楚 git 在技术上做错了什么。我的问题是这会是一个错误,git还是应该期望用户知道如何处理这种情况?

此外,更广泛的实际根本问题:为什么允许删除我的 shell 所在的目录,而如果我在已安装的目录上有 shell,则不允许弹出磁盘?对我来说似乎不一致。

恰当的例子:fuser <directory>显示当前目录的使用。如果一个程序“在”一个目录,它就是在“使用”它。

标签: linuxshellunixdirectory

解决方案


要回答您的第一个问题:

这不是一个错误,它是 Unix 系统(包括 Linux)一直以来的工作方式。

POSIX 规范中甚至还有一些不错的文本:

[EBUSY] 要删除的目录当前正在被系统或某个进程使用,实现认为这是一个错误。

即,某些实现(例如 Windows ...)可以将其称为错误,但大多数实现。即Unix变体,不要。

在文件系统中实现这一点的方式是让进程持有对代表目录的对象的引用。拥有这种引用的另一件事是父目录。git rebase删除了后者的引用,但前者仍然存在。即使重新创建了目录,它也是一个新的文件系统对象,而您的 shell 持有对旧对象的引用。

这就是为什么cd ..并且cd $(pwd)仍然有效 - 他们重新查找目录并获取新参考,并释放旧参考。

正式地,在旧引用被释放之前,旧对象不会被清理。这意味着在进程释放旧的工作目录之前,不会从磁盘中删除目录的元数据。

回答你的第二个问题:

为了弹出驱动器,您必须卸载挂载点,如上所述,您找到的进程fuser保留对该挂载点的引用。

与目录引用一样,在删除所有对它的引用之前,无法清除数量点。这就是为什么您无法弹出具有引用的驱动器的原因。

一致的做法是允许从目录树中分离文件系统而无需实际清理。您仍然无法弹出驱动器,但至少您可以根据需要使用目录树。

嗯,这实际上是可能的umount --lazy


推荐阅读