为什么我必须从已删除的目录CD出来?


19

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

/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...

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


5
您是否考虑过删除代码目录中的文件,而不是代码目录本身?
2014年

9
您误认为新创建的目录run与旧目录相同。它只有相同的名称和父目录。与此相比,您将旧车切碎,然后购买了颜色和型号完全相同的新车:您不想坐在被切碎的车上,希望最终得到一辆不受伤害的新车,对吗?
Anthon 2014年

2
Anthon:我假设路径是标识目录的路径。对我来说,“ cd ../code”是noop。我很感兴趣听到为什么不是这样。
Markus Johansson 2014年

2
@MarkusJohansson cd ../code不是傻瓜。..是您拥有或曾经拥有的路径的父级的快捷方式。如果当前目录被删除,则父路径可能仍然存在,在这种情况下,可以通过计算来访问父路径..。在该目录中搜索名称为“ code”的目录。
Anthon 2014年

2
@MarkusJohansson我强烈建议您使用任何可用的版本控制工具,而不是删除和破坏代码。共享更新(推或拉)更加容易,而意外删除错误文件的选项则更少。并且您默认保留旧版本。
2014年

Answers:


26

对我来说,“ cd ../code”是noop。我很感兴趣听到为什么不是这样。

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

当一个新的目录code中创建的,它与新的inode相关联,而这也正是它。没有保存以前删除过的文件和目录的记录,因此系统无法通过任何方法检查它曾经占用的inode并可能对其周围的东西进行重新整理以使它再次相同。这样的系统将很快变得无法使用,并且在任何情况下,都无法保证您会再次回到那里-那是不受欢迎的,因为这意味着如果创建了目录,您也可能会意外地移到其他地方需要您的(当前未使用的)inode。

我不确定是否存在最后一种可能性,或者不确定当前分配给您当前工作目录的已删除目录的inode是否被跟踪,以便在整个持续时间内都不会为其分配任何内容,等等。


3
这是真正的答案。
karan.dodia 2014年

14

在执行下一条命令之前,您的shell 不会每次都cd对上一条命令期间的路径进行操作。

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

如果目录在本地文件系统上被删除,则诸如Nautilus和Windows Explorer之类的文件浏览器通常会“向上”目录树。但是,这对于网络文件系统并非总是如此,在这种情况下,有时删除不会引起注意,并且重新出现可能会使您最终进入新目录。

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


遵循说明-从理论上讲,甚至可能存在一个文件系统,其中旧的,已删除(或更确切地说是未链接的)目录仍然存在并且可读,而新目录也已经在使用中。这在目录中实际上没有用,但在文件中却很常见。
Volker Siegel

4

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

仅当与文件系统的所有文件系统链接都消失并且没有指向该对象的文件描述符时,该文件系统对象(文件目录)才被永久销毁。

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

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

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


0

@Anthon清除了发生这种情况的原因,
作为解决方案,您可以使用alias,例如:

alias 1234='PROJECT=`pwd`; cd $PROJECT ; ./run'

bash的别名保存在〜/ .bashrc中


0

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

cd $ PWD

为了说明,我做了一个虚拟的deploy命令:

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

创建了第一个部署,将其CD编码,然后使用来检查内容,ls -lai以便您可以看到inode:

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处理的一部分。cd ..在这种情况下,某些其他/较旧的shell无法处理,您必须先cd到绝对路径。

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

镉到 $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

请注意当前目录(。)的索引节点如何更改?

如果您的部署脚本将旧目录移动到其他名称(例如mv code code.$$,在上面的.deploy脚本中),则./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'

0

我假设路径是标识目录的路径。

通往事物的道路是如何到达那里,而不是事物本身。通往床上的路径可能是穿过您的房间的,但是一旦您躺在床上,如果有人将它捡起并携带到室外,则您将不再在房间里。


0

这不是一个独立的答案,但我还有一点要说,评论的余量太小而无法容纳。

为了更好地理解相关文件系统中的目录不仅仅是路径,请尝试移动另一个进程的当前工作目录:在一个shell中,启动一个交互式Python会话:

$ python
>> import os
>> os.getcwd()
'/home/you/hocus'

然后,转到另一个shell并移动该目录:

$ cd /home/you
$ mv hocus pocus

回到原来的一个:

$蟒蛇
>>导入操作系统
>> os.getcwd()
'/ home / you / hocus'
>> os.getcwd()
'/ home / you / pocus'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.