Categories

Symlink with Directories

folderLinux 下的软链接 (英文通常称作 symbolic link, soft link 或者是 symlink) 是非常实用的东西,我想任何经常在 Linux 做事情的人都会时不时地会用到这个东西。相比起硬链接来说,它要更加灵活一些,限制也更少,而相对于 Windows 下的那种“快捷方式”来说,它又更加底层一些,处在文件系统层面,很多操作(例如打开文件 open )都会自动 follow 链接到指定的文件,因此它对大部分应用程序可以说是完全透明的。虽然如此,也有一些特殊的地方对于把 symlink 当作“自己本身”来处理还是当作“它所指向的源文件”来处理却并没有一个定论的。

最简单的例如复制文件,如果遇到 symlink 了,那么究竟是要复制一个 symlink 呢?还是要复制 symlink 指向的内容呢?这个就要看操作者自己的意图了,就不能一概而论。因此 cp 命令提供了专门的参数来控制对于 symlink 如何处理的行为。不过 scp 就要暴力一些,对于每一个 symlink 都直接 follow 过去,我之前有一次直接用 scp 来备份数据,没有注意到一个软连接,结果差点把远程主机的磁盘全部占满了。后来就改用 tar 打包再传输的办法。其实打包也是一个类似的地方:究竟是要打包链接本身呢还是链接指向的文件?其实有一些启发式的办法可以来进行猜测,例如,如果链接指向的文件也包含在打包中,那么就直接存储链接。不过这样的办法不仅实现起来麻烦,而且也有可能会猜错,毕竟这个启发式太简单了。

一个例子就是 Arch Linux 下的 Scala 打包,scala 本身结构是毕竟简单的,直接解压到任意一个地方,然后就可以调用其 bin 子目录下的可执行文件了(可能还需要设置 SCALA_HOME 环境变量),然而,根据 Linux 的习惯,可执行文件是要放在 /usr/bin/ 这样的目录下面的。因此 Arch Linux 的这个 package 在安装的时候会把实际的 bin 下面的可执行文件复制到 /usr/bin/ 下面(这也显得更加自动化一些,免得还要用户安装过后自己在 PATH 加路径才能运行),不过这破坏了 Scala 原本的结构,一些 Scala 相关的工具会假定可执行文件实际上是在 $SCALA_HOME/bin 下的,为了解决这个问题,打包人员在 package 里放了一个指向 /usr/bin 的软链接,充当 $SCALA_HOME/bin ,一切问题就解决了。好了,罗嗦了这么多,无非就是说,如果在这里打包的时候看到这个软连接的对象 /usr/bin/ 没有被包含在 package 里就直接 follow symlink 了的话,就绝对是个悲剧了! :p

废话说完了,回到本文的标题,也就是我今天碰到的问题。实际上好久以前就注意到了,但是没有太在意,甚至以为是系统的 bug ,不过后来想想系统不会有这么严重的 bug 而至今没人发现吧?于是仔细想了一下,突然恍然大悟,实验了一下,果然还是“是否 follow symlink”的问题惹的祸。例如,我们将代码的各个 release 版本都放在了一个目录下面,然后创建一个软链接指向最新的版本,这样的结构是非常常见的吧,如下所示:

drwxr-xr-x 2 pluskid pluskid 40 2009-07-27 13:01 1.0
drwxr-xr-x 2 pluskid pluskid 40 2009-07-27 13:01 2.0
drwxr-xr-x 2 pluskid pluskid 40 2009-07-27 13:01 2.1
lrwxrwxrwx 1 pluskid pluskid  3 2009-07-27 13:01 latest -> 2.1

现在我们发布了一个新版本 3.0 ,决定更新软链接:

ln -sf 3.0 latest

其中 -f 选项表示如果目标软链接已经存在了,就先删除它。命令成功执行了,按理说原来的指向 2.1 的软链接应该已经被删除了,取而代之的是一个新的指向 3.0 的软链接,可是 ls -l 一看,软链接根本没有变:

drwxr-xr-x 2 pluskid pluskid 40 2009-07-27 13:01 1.0
drwxr-xr-x 2 pluskid pluskid 40 2009-07-27 13:01 2.0
drwxr-xr-x 2 pluskid pluskid 60 2009-07-27 13:02 2.1
drwxr-xr-x 2 pluskid pluskid 40 2009-07-27 13:02 3.0
lrwxrwxrwx 1 pluskid pluskid  3 2009-07-27 13:01 latest -> 2.1

这是为什么呢?打开 2.1 这个目录一看就知道了:那里多出来了一个指向 3.0 的软连接,也叫做 3.0 。原来,这里传递给 ln 命令的 latest 在被当作一个软链接之前先被当作了一个文件夹,而 ln 当目标是一个文件夹的时候,会在该文件夹下创建一个和原始文件同名的链接。因此命令成功执行了,然而却不是我们想要的结果。

可见 symlink 虽然很方便,但是也会时不时地在一些不常注意的地方咬你一口,所以还是要非常小心的。 🙂

3 comments to Symlink with Directories