1. 程式人生 > 其它 >傻傻分不清楚的連結 fs.symlink、 ln、ln -s(詳解與應用)

傻傻分不清楚的連結 fs.symlink、 ln、ln -s(詳解與應用)

前言

寫這篇文章的原因

  1. 最近在做一些工程化相關的內容,有使用到fs.symlink(target,path), 檢視Node.js文件發現講的一般!所以這裡詳細整理下,並且記得之前在Linux下直接使用命令建立軟鏈通過ls -s source_file target_file在想這兩個引數位置有些怎麼不一致呢?
  2. 本文是我上一篇工程化系列文章中的一個小插曲,上篇文章現代前端工程化-徹底搞懂基於 Monorepo 的 lerna 模組(從原理到實戰) 有提到軟連結,那篇沒有對軟硬連結沒有進行詳細講解,只是簡單介紹了fs.symlink中軟連結建立的基本使用,本文進行詳細展開下。
  3. 軟連線的建立應用場景還是比較廣泛,比如npm link
    除錯,leran內部模組互相引用等等。

inode

在講解軟/硬連結之前,先了解一個linux系統中重要的概念inode。眾所周知,檔案儲存在硬碟上,硬碟的最小儲存單位叫做 "扇區"(Sector,每個扇區儲存512位元組).作業系統讀取硬碟的時候,不會一個個扇區地讀取,因為這樣效率太低,而是一次性連續讀取多個扇區,這種一次性讀取的連續多個扇區就是"塊"(block)。這種由多個扇區組成的"塊",是檔案存取的最小單位。"塊"的大小,最常見的是4KB,即連續八個sector組成一個block

檔案資料都儲存在"塊"中,那麼很顯然,我們還必須找到一個地方儲存檔案的元資訊,比如檔案的建立者、檔案的建立日期、檔案的大小等等。這種儲存檔案元資訊的區域就叫做inode

,中文譯名為"索引節點"

注意:我們開啟一個一個檔案時,系統首先找到檔名對應的inode號碼,然後通過inode號碼獲取inode資訊,然後根據inode資訊中的檔案資料所在block讀出資料。

上述概念的文章內容比較多,不太容易記憶,看圖!

inode 中包括了哪些內容 ?

inode包含檔案的元資訊,具體來說有以下內容:

  1. 檔案的位元組數
  2. 檔案擁有者的User ID
  3. 檔案的Group ID
  4. 檔案的讀、寫、執行許可權
  5. 檔案的時間戳,共有三個:ctimeinode上一次變動的時間,mtime指檔案內容上一次變動的時間,atime指檔案上一次開啟的時間。
  6. 連結數,即有多少檔名指向這個inode
  7. 檔案資料block的位置

linux 命令如何獲取一個檔案的 inode 資訊

可以直接使用linux命令stat檢視某個檔案的inode資訊

stat example.js

輸出資訊

Node.js 中如何獲取一個檔案的 inode 資訊

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標識碼。這裡也可以解釋可能小夥伴們覺得說不通的問題,僅修改目錄的讀許可權,並不能實現讀取目錄下所有檔案內容的原因,最後需要通過遞迴目錄下的檔案來進行修改

軟連結與硬連結

什麼是軟連結(soft link,也叫符號連結)

軟連結類似於Window中的 “快捷方式” 。建立軟連結會建立一個新的inode,比如為檔案a建立了軟連結檔案b,檔案b內部會指向ainode。當我們讀取檔案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檔案,軟連結建立成功。

Node.js 中建立軟連結

基礎用法

Node.js官方文件提供了symlinkSync函式建立軟連結。

fs.symlinkSync(target,path,type)

target <string> | <Buffer> | <URL>   // 目標檔案
path <string> | <Buffer> | <URL>  // 建立軟鏈對應的地址
type <string>

它會建立名為path的連結,該連結指向targettype引數僅在Windows上可用,在其他平臺上則會被忽略。它可以被設定為'dir''file''junction'。如果未設定type引數,則Node.js將會自動檢測target的型別並使用'file''dir'。如果target不存在,則將會使用'file'Windows上的連線點要求目標路徑是絕對路徑。當使用'junction'時,target引數將會自動地標準化為絕對路徑。

  • 使用示例
const res = fs.symlinkSync('./target/a.js','./b.js');

這段程式碼的意思是為 建立一個軟連結b.js指向了檔案./targert/a.js,當a.js中的內容發生變化時,b.js檔案也會發生相同的改變。

⚠️注意:如果對目錄建立軟連結,方法中第三個引數需要傳'dir'(雖然第三個引數只在windows下生效,這樣傳遞可以確保跨平臺不會出現問題):

fs.symlink(target,path,'dir')

上面講解了linuxNode.js建立軟連結的兩種方式,認真看的小夥伴可能發現問題,為什麼Node.jsfs.symlink(target,path)shell命令中的ln -s source target兩個引數好像是反的呢,是不是會有這樣的疑問?

其實如果前面的小例子你都嘗試了,會發現後面傳遞的兩個引數順序實際是一致的,都是讓後面新建的的"指向=>"前面的哦!

什麼是硬連結(hard link)

一般情況,一個檔名"唯一"對應一個inode。但是linux允許多個檔名都指向同一個inode。表示我們可以使用不同對檔名訪問同樣的內容;對檔案內容進行修改將放映到所有檔案;刪除一個檔案不影響另一個檔案對訪問。這種機制就被稱為"硬連結"

硬連結的建立

可以直接使用linux命令ln source target來建立硬連結(注意:source已存在的檔案,target是將要建立的連結)

ln ./target/a.js c.js

執行shell命令後,會出現c.js檔案,硬連結建立成功。

與軟連線不同,只能給檔案建立硬連結,不能給目錄建立硬連結。並且source檔案必須存在,否則建立硬連結時會報錯。

刪除一個檔案不會影響另一個檔案的訪問。原因是什麼?實際上,檔案inode中還有一個連結數的資訊,每多一個檔案指向這個inode,該數字就會加1,每少一個檔案指向這個inode,該數字就會減1,當值減到 0,系統就自動回收inode及其對應的block區域。很像是一種引用計數的垃圾回收機制。

當我們對某個檔案建立了硬連結後,對應的inode的連結數會是``2(原檔案本身已經有一個指向),當刪除一個檔案時,連結數變成1`,並沒達到回收的條件,所以我們還是可以訪問檔案。

軟連結與硬連結對比分析總結

  1. 使用 ln source target 建立硬連結;使用 ln -s source target 建立軟連結

  2. 硬連結不會建立額外 inode,和原始檔共用同一個 inode;軟連結會建立額外一個檔案(額外 inode),指向原始檔的 inode

  3. 建立硬連結時,source 必須存在且只能是檔案;建立軟連結時,source 可以不存在而且可以是目錄

  4. 刪除原始檔不會影響硬連結檔案的訪問(因為 inode 還在);刪除原始檔會影響軟連結檔案的訪問(因為指向的 inode 已經不存在了)

  5. 對於已經建立的同名連結,不能再次建立,除非刪掉或者使用 -f 引數

應用場景

npm link

npm link的原理也是通過軟鏈來實現的。當我們想要除錯本地開發的npm模組包(還沒有釋出或者修改了一些內容)時,需要使用npm link來進行除錯 舉個例子:

有兩個專案create-mono-repocreate-mono-repo-testClicreate-mono-repo專案節點下執行npm link成功後 ,在create-mono-repo-testCli專案目錄下執行npm link create-mono-repo這樣就可以完成除錯了

lerna

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

轉自https://mp.weixin.qq.com/s/sd6LSSNbZNh2JhDucdhcIQ