Linux中的Link及Node中几种常见的软链接应用

515 阅读7分钟

前言

从一个命令开始我们今天的话题: vue create

通过vue create创建项目,并使用淘宝源来安装,我们会输入如下的命令:

vue create vue-test-app -r https://registry.npm.taobao.org

首先来拆解一下这个命令

上面这个命令有4个部分组成:

  • 主命令:vue
  • command: create
  • command 的 param: vue-test-app
  • command 的 option,用来辅助命令确认在特定场景下用户的选择(可以理解为配置)

这里的 -r是 option, -r其实是简写,等同于 --registry

进入正题:输入这个命令后,究竟发生了什么事情?

which vue
/usr/local/bin/vue

cd /usr/local/bin/vue
ls -ll
lrwxr-xr-x  1 root  wheel  39  1  5 11:08 vue -> ../lib/node_modules/@vue/cli/bin/vue.js

我们首先在查找vue命令的位置,发现在 /usr/local/bin/vue 这个目录

进入这个目录之后,输出所有文件信息

得到了他是链接文件,链接到实际的的文件是 ../lib/node_modules/@vue/cli/bin/vue.js

链接文件,这是什么?

软链接与硬链接

注:本章内容节选自详解 Linux 中的硬链接与软链接

在开始介绍软链接和硬链接之前,我们首先介绍一下文件系统

在 Linux 文件系统中,一个文件被分成两个部分:元数据(metadata)与用户数据(user data)。

元数据为文件的附加属性,如索引节点(Inode)、文件大小、文件创建时间、文件所有者等。

「元数据中并不包含文件名,文件名仅仅是为了方便用户使用。Linux 文件系统为每一个保存在磁盘分区中的文件(无论什么类型)都分配一个索引节点号(Inode Number),索引节点号是文件在一个文件系统中的唯一标识,不同文件所对应的索引节点号是不相同的;」

用户数据,即文件数据块(data block),文件数据块中以二进制的形式记录着文件的真实内容。

文件系统

软链接 ---- ln -s 源文件 链接文件

软链接(也称符号链接)可以看成是一个普通的文件, 「只不过这个文件中的数据块存放的是源文件的索引节点号。」 可以通过为ln指令添加-s(s: soft 的意思)选项创建软链接:

ln -s 源文件 链接文件

为 hello.txt 文件创建一个软链接:

chenkc:~$ ln -s hello.txt softlink_hello.txt
chenkc:~$ ls -il
total 675236
1837900 -rw-r--r--  1 chenkc chenkc        36 8-р с 25 18:55 hello.txt
6705 lrwxrwxrwx  1 chenkc chenkc         9 8-р с 25 18:57 softlink_hello.txt -> hello.txt
...

源文件的索引节点号为 1837900,而软链接文件的索引节点号为 6705,显然源文件和软链接文件并不是同一个文件,其实从两个文件的大小也可以看出来,其中源文件大小为 36 个字节,而软链接文件大小仅仅为 9 个字节。

软链接

硬链接 ---- ln 源文件 链接文件

在 Linux 中允许多个文件名指向同一个索引节点号,而硬链接(hard link, 也称链接)指的是通过索引节点号进行的链接。可以通过ln指令为文件创建硬链接:

ln 源文件 链接文件

为 hello.txt 文件创建两个硬链接:

chenkc:~$ ln hello.txt hardlink_hello.txt
chenkc:~$ ln hello.txt hardlink_hello2.txt
chenkc:~$ ls -il
total 675432
1837900 -rw-r--r--  3 chenkc chenkc        56 8-р с 24 21:34 hello.txt
1837900 -rw-r--r--  3 chenkc chenkc        56 8-р с 24 21:34 hardlink_hello.txt
1837900 -rw-r--r--  3 chenkc chenkc        56 8-р с 24 21:34 hardlink_hello2.txt
...

源文件和两个硬链接文件的索引节点号都为 1837900,而索引节点号为文件的唯一标识,「因此源文件和硬链接文件是仅文件名不同的相同文件,创建文件的硬链接相当于为文件又起了一个新的文件名。」

硬链接

此时的 hello.txt、hardlink_hello.txt 以及 hardlink_hello2.txt 拥有相同的索引节点号,查看文件内容也会发现为同一个文件

软链接的应用

运用软链接的知识,我们回到最初的话题,当我们输入 vue create vue-test-app命令的时候,终端究竟在做什么

下图是我们软链的链接过程

  • 终端在全局环境变量中找到 vue命令 which vue
  • 终端根据vue命令链接到实际的文件 lib/node_modules/@vue/cli中的bin/vue.js
  • 终端利用node执行 vue.js
  • vue.js 解析command options
  • vue.js 执行command
  • 执行完毕,退出

总结一下:vue脚手架命令的全过程,如下图

我们接下来对这个问题进行一点深入:

1、问:为什么全局安装 @vue/cli 后,就会添加命令为 vue ?

npm install -g @vue/cli

可以查看 node_modules/@vue/cli 中的 package.json

"bin": {
	"vue": "bin/vue.js"
},

在项目中安装 @vue/cli 的时候,由于 bin 的配置,会在环境变量(全局或者局部)中按照 bin 的配置生成一个软链接,例如:

/user/local/bin/vue  --->  lib/node_modules/@vue/cli/bin/vue.js

符号链接和package.json中的bin属性

2、问:为什么 vue 指向一个 js文件,我们可以直接通过 vue命令直接执行它?

#!/usr/bin/env node

.... // 代码块

因为文件添加了这句话告诉了系统在我们的环境变量中找到 node命令 解析

/usr/bin/env 命令可以找到所有的命令,其中包含 node 命令

/usr/bin/env node test.js

等价于
node test.js

等价于
#!/usr/bin/env node
...

3、问:怎么实现,添加一个全局命令"test",当在终端输入"test",执行hello.js的内容

// hello.js
#!/usr/bin/env node
console.log('已经执行了hello.js')

/usr/local/bin 下配置,/XXX/hello.js 是这个文件的路径

ln -s /XXX/hello.js test
 /usr/local/bin  ls -il
total 0
6760614 lrwxr-xr-x  1 root  wheel  29  1 12 19:51 test -> XXX/hello.js

然后在其他任何目录下 执行 test 就会执行 hello.js 文件

 ~ test
已经执行了hello.js

npm link

npm link 其实与软链接也有很大的关系,我们接下来了解一下这个命令

npm link 官方文档

npm link是一种把包链接到包文件夹的方式,即:可以在不发布npm模块的情况下,调试该模块,并且修改模块后会实时生效,不需要通过npm install进行安装

从上面的vue命令的学习中,很容易就能知道

而当我们在 npm install -g 的时候,其实是将相关文件安装在 /usr/local/lib/node_modules 目录下,同时在全局命令 /usr/local/bin 目录下会有一个映射脚本,将其指向 /usr/local/lib 下的真实文件。

同样的,npm link 做的事情也是一样,唯一的区别是,它在 /usr/local/lib 下的 node_modules 里不是存的真实的文件,而是存了一个软链接,指向你当前执行 npm link 的目录。如果开发的的是node包,则执行的命令名和真实执行的文件入口,会根据项目的 package.jsonbin 的配置来获取。

如何使用

cd ~/projects/node-redis    # go into the package directory

# 全局链接,根据node-redis目录下package.json中的bin的配置
#生成一个软链接,指向~/projects/node-redis目录
#然后全局都可以使用命令redis
npm link

# 进入另一个目录,这个目录依赖安装包 node-redis
cd ~/projects/node-bloggy

# 会在当前目录的node_modules下生成一个软链接,指向redis,redis也是一个软链接,指向~/projects/node-redis
npm link redis              # link-install the package

这样就能实现当修改node-redis包,不需要重新安装,依赖这个包的 node-bloggy 也能实时生效了

取消链接

cd ~/projects/node-bloggy
npm unlink redis
cd ~/projects/node-redis
npm unlink

lerna link

// lerna link 源码
function createSymlink(src, dest, type) {
  log.silly("createSymlink", [src, dest, type]);

  if (process.platform === "win32") {
    return createWindowsSymlink(src, dest, type);
  }

  return createPosixSymlink(src, dest, type);
}

function createSymbolicLink(src, dest, type) {
  log.silly("createSymbolicLink", [src, dest, type]);

  return fs
    .lstat(dest)
    .then(() => fs.unlink(dest))
    .catch(() => {
      /* nothing exists at destination */
    })
    .then(() => fs.symlink(src, dest, type));
}

简单看一下源码:

使用 fs.symlink创建软链接,& chmod等等

内部其实还是这一套 npm link

fs.symink

在 node 中,我们可以使用方法 fs.symink(target, path) 建立软链接(符号链接),没有直接的方法建立硬链接(就算通过子进程的方式直接指向 shell 命令也不能跨平台)

总结

软链硬链在很多地方都有应用,例如:软链接应用之快捷方式,硬链接应用之文件多人共享等等,大家可以多多挖掘。

参考资料