写这篇文章的原因
fs.symlink(target,path)
, 查看 Node.js
文档发现讲的一般!所以这里详细整理下,并且记得之前在 Linux
下直接使用命令创建软链通过 ls -s source_file target_file
在想这两个参数位置有些怎么不一致呢?fs.symlink
中软链接创建的基本使用,本文进行详细展开下。npm link
调试, leran
内部模块互相引用等等。在讲解软/硬链接之前,先了解一个 linux
系统中重要的概念 inode
。众所周知,文件存储在硬盘上,硬盘的最小存储单位叫做 "扇区"( Sector
,每个扇区存储 512
字节).操作系统读取硬盘的时候,不会一个个扇区地读取,因为这样效率太低,而是一次性连续读取多个扇区,这种一次性读取的连续多个扇区就是"块"( block
)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最常见的是 4KB
,即连续八个 sector
组成一个 block
。
文件数据都储存在 "块"
中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做 inode
,中文译名为 "索引节点"
。
注意:我们打开一个一个文件时,系统首先找到文件名对应的
inode
号码,然后通过inode
号码获取inode
信息,然后根据inode
信息中的文件数据所在block
读出数据。
上述概念的文章内容比较多,不太容易记忆,看图!
image.png
inode包含文件的元信息,具体来说有以下内容:
User ID
Group ID
ctime
指 inode
上一次变动的时间,mtime
指文件内容上一次变动的时间,atime
指文件上一次打开的时间。inode
block
的位置可以直接使用 linux
命令 stat
查看某个文件的 inode
信息
stat example.js
输出信息
xiaohun node % stat example.js
16777223 11904170 -rw-r--r-- 1 xiaohun staff 0 0 "Mar 24 13:46:08 2021" "Mar 24 13:46:08 2021" "Mar 24 13:46:26 2021" "Mar 24 13:46:08 2021" 4096 0 0x40 example.js
在 Node.js
中,fs
提供了 stat
函数查看相关信息
fs.statSync('./example.js');
输出信息
文件信息 Stats {
dev: 16777223,
mode: 33188,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 11904170,
size: 0,
blocks: 0,
atimeMs: 1616564768255.48,
mtimeMs: 1616564768255.48,
ctimeMs: 1616564786778.5532,
birthtimeMs: 1616564768255.48,
atime: 2021-03-24T05:46:08.255Z,
mtime: 2021-03-24T05:46:08.255Z,
ctime: 2021-03-24T05:46:26.779Z,
birthtime: 2021-03-24T05:46:08.255Z
}
每一个 inode
都有一个唯一的标识码 ,上面的输出信息中 ino
就是 inode
的唯一标识码,在 linux
系统内部使用 inode
的标识码来识别文件,并不使用文件名。之前系的
在 linux
系统中,目录也是一种文件。目录文件包含一系列目录项,每一个目录项由两部分组成:所包含文件的文件名,以及文件名对应的 inode
标识码。我们可以使用 ls -i
来列出目录中的文件以及所有的 inde
标识码。这里也可以解释可能小伙伴们觉得说不通的问题,仅修改目录的读权限,并不能实现读取目录下所有文件内容的原因,最后需要通过递归目录下的文件来进行修改。
image.png
软链接类似于 Window
中的 “快捷方式” 。创建软链接会创建一个新的 inode
,比如为文件 a
创建了软链接文件b,文件 b
内部会指向 a
的 inode
。当我们读取文件b的时候,系统会自动导向文件 a
,文件 b
就是文件 a
软连接(或者叫符号链接)。
a
的内容,文件 b
的内容也会发生改变,对文件内容的修改向放映到所有文件。a
时,在访问软连接文件b是,会报错 "No such file or directory"
可以直接使用 linux
命令 ln -s source target
来创建软链接(注意:表示 target
"指向" source
)
ln -s ./target/a.js b.js
执行 shell
命令后,会出现 b.js
文件,软链接创建成功。
image.png
Node.js
官方文档提供了 symlinkSync
函数创建软链接。
fs.symlinkSync(target,path,type)
target <string> | <Buffer> | <URL> // 目标文件
path <string> | <Buffer> | <URL> // 创建软链对应的地址
type <string>
它会创建名为 path
的链接,该链接指向 target
。type
参数仅在 Windows
上可用,在其他平台上则会被忽略。它可以被设置为 'dir'
、 'file'
或 'junction'
。如果未设置 type
参数,则 Node.js
将会自动检测 target
的类型并使用 'file'
或 'dir'
。如果 target
不存在,则将会使用 'file'
。Windows
上的连接点要求目标路径是绝对路径。当使用 'junction'
时, target
参数将会自动地标准化为绝对路径。
const res = fs.symlinkSync('./target/a.js','./b.js');
image.png
这段代码的意思是为 创建一个软链接 b.js
指向了文件 ./targert/a.js
,当 a.js
中的内容发生变化时,b.js
文件也会发生相同的改变。
⚠️注意:如果对目录创建软链接,方法中第三个参数需要传'dir'(虽然第三个参数只在windows下生效,这样传递可以确保跨平台不会出现问题):
fs.symlink(target,path,'dir')
上面讲解了 linux
和 Node.js
创建软链接的两种方式,认真看的小伙伴可能发现问题,为什么 Node.js
中 fs.symlink(target,path)
和 shell
命令中的 ln -s source target
两个参数好像是反的呢,是不是会有这样的疑问?
其实如果前面的小例子你都尝试了,会发现后面传递的两个参数顺序实际是一致的,都是让后面新建的的"指向=>"前面的哦!
一般情况,一个文件名"唯一"对应一个 inode
。但是 linux
允许多个文件名都指向同一个 inode
。表示我们可以使用不同对文件名访问同样的内容;对文件内容进行修改将放映到所有文件;删除一个文件不影响另一个文件对访问。这种机制就被称为"硬链接"
可以直接使用 linux
命令 ln source target
来创建硬链接(注意:source
已存在的文件,target
是将要建立的链接)
ln ./target/a.js c.js
执行 shell
命令后,会出现 c.js
文件,硬链接创建成功。
image.png
与软连接不同,只能给文件建立硬链接,不能给目录建立硬链接。并且source文件必须存在,否则创建硬链接时会报错。
删除一个文件不会影响另一个文件的访问。原因是什么?实际上,文件 inode
中还有一个链接数的信息,每多一个文件指向这个 inode
,该数字就会加 1
,每少一个文件指向这个 inode
,该数字就会减 1
,当值减到 0,系统就自动回收 inode
及其对应的 block
区域。很像是一种引用计数的垃圾回收机制。
当我们对某个文件建立了硬链接后,对应的 inode
的链接数会是`2
(原文件本身已经有一个指向),当删除一个文件时,链接数变成1
,并没达到回收的条件,所以我们还是可以访问文件。
npm link
的原理也是通过软链来实现的。当我们想要调试本地开发的 npm
模块包(还没有发布或者修改了一些内容)时,需要使用 npm link
来进行调试 举个例子:
image.png有两个项目 create-mono-repo
和 create-mono-repo-testCli
在 create-mono-repo
项目节点下执行 npm link
成功后 ,在 create-mono-repo-testCli
项目目录下执行 npm link create-mono-repo
这样就可以完成调试了
lerna
创建的项目, packages
目录下各模块互相依赖也是基于 fs.symlinkSync
创建软链接实现的。
具体实现代码和地址如下:
源码对应地址 https://github.com/lerna/lerna/tree/main/utils/create-symlink
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));
}
参考文章
1 . 关于inode的讲解 https://www.ruanyifeng.com/blog/2011/12/inode.html
2 . lerna 源码 https://github.com/lerna/lerna
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8