为什么我必须从已删除的目录中退出?

为什么我必须从已删除的目录中退出?

在我的服务器上,我有一个看起来像这样的目录结构:

/myproject/code

我通常有一个到服务器的 ssh 连接并“站在”该目录中:

root@machine:/myproject/code#

当我部署新版本的代码时,代码目录将被删除,因此我只剩下:

root@machine:/myproject/code# ./run
-bash: ./run: No such file or directory

我发现的唯一解决方案是 cd 退出并返回:

root@machine:/myproject/code# cd ../code
root@machine:/myproject/code# ./run
Running...

我可以避免这种情况吗?这是一个有点奇怪的行为。如果您有一个很好的解释为什么会发生这种情况,我将不胜感激。

答案1

对我来说,“cd ../code”是一个无用的东西。我很想听听为什么不是这样。

因为文件和目录本质上是文件系统索引节点,而不是名称——这可能是特定于文件系统类型的实现细节,但对于所有 ext 系统都是如此,所以我将在这里坚持下去。

当一个新的目录code创建后,它与一个新的索引节点相关联,这就是它所在的位置。没有保存以前删除的文件和目录的记录,因此系统无法检查它曾经占用的索引节点,也无法重新整理内容以使其再次相同;这样的系统很快就会变得无法工作,并且在任何情况下,可能都不能保证您会再次回到那里——这有点不可取,因为这意味着如果创建了目录,您也可能意外地到达其他地方这需要你的(当前未使用的)inode。

我不确定这最后一种可能性是否存在,或者当前分配给当前工作目录的已删除目录的索引节点是否被跟踪,以便在持续时间内不会分配任何内容,等等。

答案2

你的壳每次cd执行下一个命令之前,都会对上一个命令期间所在的路径执行 a 操作。

您删除了当前目录并创建了一个同名目录,这不是同一个目录,只是具有相同名称/路径的目录。

如果本地文件系统上的目录被删除,Nautilus 和 Windows 资源管理器等文件浏览器通常会“向上”目录树。然而,对于网络文件系统来说,情况并非总是如此,在这种情况下,有时删除不会被注意到,并且重新出现可能会让您最终进入新目录。

shell 可以cd在执行下一个命令之前进入当前目录,我不知道有任何这样做(或可以配置为这样做)。

答案3

在大多数类 UNIX 系统上,进程的“当前目录”作为指向该目录的文件描述符存储在内核中。内核实际上并不存储当前目录的路径:该信息由 shell 跟踪。

文件系统对象(文件或者目录)只有当所有文件系统链接都消失时才会被永久销毁,没有文件描述符指向该对象。

因此,如果删除一个目录,而仍有一个进程将其保留为当前工作目录,则该进程将cwd阻止该目录被真正删除。锚定目录的文件系统链接(其在父目录中的条目及其所有内容)将消失,但目录本身将继续作为一种“僵尸”存在。同时,您可以在与旧目录相同的位置创建一个全新的目录,这是一个完全不同的文件系统对象,但共享相同的路径。

因此,当您这样做时cd ../code(或者,在许多 shell 上cd .),您实际上是在遍历文件系统层次结构并转到新的位于旧地址的目录。

以此类推,删除目录就像强行将房屋移至垃圾场(断开与先前地址的联系)。如果还有人住在那里(将其用作自己的cwd),他们就必须在房子被夷为平地之前离开。与此同时,可以在旧地址建造一栋全新的房子。

答案4

确认当前工作目录是基于索引节点号,而不是您查找到的目录号。由于您使用的是 bash,因此可以使用 $PWD 来 cd 到同名的新目录:

cd $PWD

为了说明这一点,我做了一个虚拟部署命令:

set -x
cd ~/tmp
rm -rf code
mkdir code
echo echo hello from $* > code/run
chmod +x code/run

创建第一个部署,cd到代码,然后检查内容,ls -lai以便您可以看到索引节点:

ianh@abe:~/tmp$ ./,deploy first
++ cd /home/ianh/tmp
++ rm -rf code
++ mkdir code
++ echo echo hello from first
++ chmod +x code/run
ianh@abe:~/tmp$ cd code
ianh@abe:~/tmp/code$ ls -lai
total 12
22945913 drwxr-xr-x  2 ianh ianh 4096 Apr  9 23:12 .
22937618 drwxrwxr-x 14 ianh ianh 4096 Apr  9 23:12 ..
22939455 -rwxr-xr-x  1 ianh ianh   22 Apr  9 23:12 run

现在运行第二次部署

ianh@abe:~/tmp/code$ ../,deploy 2nd
++ cd /home/ianh/tmp
++ rm -rf code
++ mkdir code
++ echo echo hello from 2nd
++ chmod +x code/run

并检查目录内容...现在目录中没有任何内容!甚至不 '。'和 '..'!从这里您可以看到,当您运行时,bash 没有使用“..”目录条目,cd ..因为“..”不再存在 - 我认为它是 $PWD 处理的一部分。一些其他/较旧的 shell 无法处理cd ..这种情况,您必须首先 cd 到绝对路径。

ianh@abe:~/tmp/code$ ls -lai
total 0

Cd$PWD并重试:

ianh@abe:~/tmp/code$ cd $PWD
ianh@abe:~/tmp/code$ ls -lai
total 12
22945914 drwxr-xr-x  2 ianh ianh 4096 Apr  9 23:12 .
22937618 drwxrwxr-x 14 ianh ianh 4096 Apr  9 23:12 ..
22939455 -rwxr-xr-x  1 ianh ianh   20 Apr  9 23:12 run
ianh@abe:~/tmp/code$ ./run
hello from 2nd

请注意当前目录 (.) 的 inode 是如何变化的?

如果您的部署脚本将旧目录移动到其他名称,例如mv code code.$$在上面的部署脚本中,那么./run就可以工作,直到你使用cd $PWD你会运行老的代码,不是新的。

ianh@abe:~/tmp/code$ ./run
hello from 2nd
ianh@abe:~/tmp/code$ ../,deploy 3rd
++ cd /home/ianh/tmp
++ '[' -d code ']'
++ mv code code.9629
++ mkdir code
++ echo echo hello from 3rd
++ chmod +x code/run
ianh@abe:~/tmp/code$ ./run
hello from 2nd
ianh@abe:~/tmp/code$ cd $PWD
ianh@abe:~/tmp/code$ ./run
hello from 3rd

使用 capistrano 进行部署也有同样的问题(它们有一个从当前名称到当前版本的符号链接),因此我使用别名 cd 到生产/暂存区域并适当设置 RAIL_ENV:

alias cdp='export RAILS_ENV=production; echo RAILS_ENV=$RAILS_ENV ; cd /var/www/www.example.com/current'
alias cds='export RAILS_ENV=staging; echo RAILS_ENV=$RAILS_ENV ; cd /var/www/staging.example.com/current'

相关内容