# 理解 Symbolic Link、Hard Link 與 Directory Junction 的差異之處

我經常利用不同的**連結**(Link)來管理檔案和目錄，以確保系統的設定隨時可以透過雲端硬碟同步與備份，相當便利。在 Windows 系統中有三種不同的連結方式，分別是 **符號連結**(`Symbolic Link`)、**硬連結**(`Hard Link`)和**目錄聯接**(`Directory Junction`)。這三種連結方式各有其**特性**和**用途**，所以我打算針對這三種連結方式進行比較，幫助大家更好地理解它們之間的差異。

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a18d1709b52e02e8eed1-c19cfa55-5b23-4a88-88f9-46b5511771c8.webp)

### TL;DR

這裡我就以 [Notepad4](https://github.com/zufuliu/notepad4) 應用程式為範例，來說明如何使用**目錄聯接**來管理應用程式的設定檔，讓特定應用程式的設定可以快速的在多台電腦自動同步設定，這也是我常用的一個技巧。

1.  先透過 [Chocolatey](https://community.chocolatey.org/packages/notepad4) 安裝軟體
    
    ```bash
    choco install notepad4 -y
    ```
    
2.  將設定檔搬移到 Dropbox 雲端硬碟
    
    ```bash
    move "%LOCALAPPDATA%\Notepad4" "%USERPROFILE%\Dropbox\Notepad4"
    ```
    
3.  建立目錄聯接
    
    ```bash
    mklink /J "%LOCALAPPDATA%\Notepad4" "%USERPROFILE%\Dropbox\Notepad4"
    ```
    

大功告成！這樣你就可以在多台電腦上同步 Notepad4 的設定檔了。

你的第二台電腦只要這樣做：

1.  先透過 [Chocolatey](https://community.chocolatey.org/packages/notepad4) 安裝軟體
    
    ```bash
    choco install notepad4 -y
    ```
    
2.  刪除原本的設定檔
    
    ```bash
    rmdir /s /q "%LOCALAPPDATA%\Notepad4"
    ```
    
3.  建立目錄聯接
    
    ```bash
    mklink /J "%LOCALAPPDATA%\Notepad4" "%USERPROFILE%\Dropbox\Notepad4"
    ```
    

若想要查詢特定資料夾或檔案是否為連結，可以透過 PowerShell 來查詢，以下命令可以幫助你快速找出所有已經設定好的連結：

```powershell
Get-ChildItem -Path "$env:APPDATA" -Recurse -Directory | Where-Object {$_.Attributes -band [System.IO.FileAttributes]::ReparsePoint} | Format-List FullName, Mode, Target, Attributes
```

```powershell
Get-ChildItem -Path "$env:USERPROFILE\Documents" -Recurse -Directory | Where-Object {$_.Attributes -band [System.IO.FileAttributes]::ReparsePoint} | Format-List FullName, Mode, Target, Attributes
```

以下正文開始：

### 符號連結 (Symbolic Link)

**符號連結** (Symbolic Link)，亦稱為**軟連結** (Soft Link)，是一種包含另一個**檔案路徑**或**目錄路徑**的檔案，可以指向**目標物**件的**完整路徑**或**相對路徑**。

在 Windows 系統中，可以使用 `mklink` 命令來建立**符號連結**，但你需要注意的地方是，這個 `mklink` 並不是一個執行檔，而是在 Command Prompt (命令提示字元) 下的一個命令。

1.  建立指向**檔案**的符號連結
    
    這個 `mklink` 命令的第一個參數是指定符號連結檔案的路徑，是我們即將建立的「符號連結檔」，該檔案不能已經存在。而第二個參數必須指向一個已經存在的檔案。
    
    ```batch
    mklink "符號檔案路徑" "真實檔案路徑"
    ```
    
    我以 [Notepad4](https://github.com/zufuliu/notepad4) 設定檔為例，建立一個指向 `Notepad4.ini` 設定檔的符號連結：
    
    ```batch
    mklink "%LOCALAPPDATA%\Notepad4\Notepad4.ini" "%USERPROFILE%\Dropbox\Notepad4\Notepad4.ini"
    ```
    
2.  建立指向**目錄**的符號連結
    
    你也可以將整個設定檔所在的目錄直接建立符號連結到 Dropbox 雲端硬碟的目錄，加上 `/D` 參數即可：
    
    ```batch
    mklink /D "%LOCALAPPDATA%\Notepad4" "%USERPROFILE%\Dropbox\Notepad4"
    ```
    
    > 我的作法都是先將原本路徑的檔案或資料夾，先全部用「搬移」的方式挪動到雲端硬碟，再透過符號連結的方式，將**原本的路徑**指向**雲端硬碟的路徑**。
    

使用**符號連結**的時候要注意以下特性：

-   **靈活性高**：可以跨越**不同的檔案系統**或**磁碟分割區**，連接到**網路磁碟**也可以。
-   **依賴性高**：若**目標檔案**被**移動**或**刪除**，符號連結將會失效，因為無法無法存取**目標**內容。
-   **辨識方式**：在**檔案總管**中，符號連結通常具有特殊的圖示標示為連結，肉眼可以清晰辨識檔案或目錄是否為**符號連結**。

### 硬連結 (Hard Link)

**硬連結** (Hard Link) 是直接指向**檔案**的**索引節點** (`inode`) 的另一個**目錄項目**，實質上使**多個檔案名稱**指向**同一份資料**。

也因為**硬連結**直接指向**檔案**的**索引節點** (`inode`)，所以**硬連結**無法跨越**不同的檔案系統**或**磁碟分割區**，且**硬連結**無法建立在**目錄**上，就只有**檔案**可以設定**硬連結**。

在 Windows 系統中，你也可以使用 `mklink` 命令來建立**硬連結**，但你需要注意的地方是，這個 `mklink` 並不是一個執行檔，而是在 Command Prompt (命令提示字元) 下的一個命令。

在 Windows 系統中，同樣使用 `mklink` 命令，但需加上 `/H` 參數來建立硬連結：

```batch
mklink /H "硬連結檔案路徑" "真實檔案路徑"
```

我以 [Notepad4](https://github.com/zufuliu/notepad4) 設定檔為例，建立一個指向 `Notepad4.ini` 設定檔的符號連結：

```batch
mklink /H "%LOCALAPPDATA%\Notepad4\Notepad4.ini" "%USERPROFILE%\Dropbox\Notepad4\Notepad4.ini"
```

使用**硬連結**的時候要注意以下特性：

-   **共享資料**：所有的硬連結指向相同的資料，只要有至少一個連結存在，資料就不會被刪除。
-   **限制**：
    -   只能連結檔案，無法連結目錄。
    -   必須在同一個磁碟分割區內建立，無法跨越不同的磁碟或網路位置。
-   **透明性**：硬連結從**檔案總管**看起來和原始檔案一樣，無法從外觀上區分。

### 目錄聯接 (Directory Junction)

**目錄聯接** (Directory Junction) 是一種專門用於**連結目錄**的連結方式，允許**不同磁碟分割區**內的**目錄**相互連結。

在 Windows 系統中，你依然可以使用 `mklink` 命令，但需加上 `/J` 參數來建立目錄聯接：

```batch
mklink /J "目錄聯接路徑" "真實目錄路徑"
```

我以 [Notepad4](https://github.com/zufuliu/notepad4) 設定檔為例，將設定檔的資料夾指向到 Dropbox 雲端硬碟：

```batch
mklink /J "%LOCALAPPDATA%\Notepad4" "%USERPROFILE%\Dropbox\Notepad4"
```

使用**目錄聯接**的時候要注意以下特性：

-   **目標限制**：僅可連結目錄，無法連結單一檔案。
-   **同台電腦**：目錄聯接必須指向本地系統內的目錄，無法指向網路位置。
-   **依賴性高**：若目標目錄被移動或刪除，聯接將失效，但可以透過重新建立聯接來恢復\[9\]\[20\]。

### 不同連結之間的差異比較

下表列出了**符號連結**、**硬連結**和**目錄聯接**之間的差異：

| **特性** | **符號連結  <br>(Symbolic Link)** | **硬連結  <br>(Hard Link)** | **目錄聯接  <br>(Directory Junction**) |
| :--- | :--- | :--- | :--- |
| **可連結對象** | **檔案**及**目錄** | **僅檔案** | **僅目錄** |
| **跨磁碟分割** | **支援** | 不支援 | **支援** (僅限同台電腦) |
| **依賴性** | 目標移動或刪除會導致連結失效 | **目標被刪除不影響，因為指向相同資料** | 目標移動或刪除會導致聯接失效 |
| **辨識方式** | **有特殊圖示標示為連結** | 與原檔案相同，無法區分 | **有特殊圖示標示為連結** |
| **創建命令** | `mklink` 或 `mklink /D` | `mklink /H` | `mklink /J` |
| **適用情境** | 需要**跨磁碟**或建立靈活連結時 | 在同一磁碟內需要**多個檔案名稱**指向**同一資料**時 | 需要**連結不同目錄結構**，但在同一台電腦上時 |

透過上述比較，你可以根據實際需求選擇最適合的連結方式。我自己會這樣總結：

-   最萬用的，就是**符號連結**了，沒什麼使用上的限制，有選擇障礙就選這個就好！
-   若需在**多個檔案**指向**同一份實體檔案內容**，且限制在同一分割區內，使用**硬連結**則更為合適，可以減少硬碟空間耗用。
-   如果需要**連結目錄**，只要是在同一台電腦上，無論幾顆硬碟、幾個磁區，選擇**目錄聯接**是最佳選擇。

補充一點很關鍵的知識，那就是**目錄聯接**對系統和應用程式來說是完全透明的，應用程式無法察覺這是一個連結，但**符號連結**和**硬連結**則不同，應用程式可以察覺到這是一個連結，這是需要注意的地方。這是為什麼我都用**目錄聯接**來管理應用程式的設定檔的原因！👍

### 查詢特定資料夾或檔案是否為連結

在 Windows 系統中，你可以透過 PowerShell 來查詢目前電腦設定了多少「連結」，以下命令可以幫助你快速找出所有已經設定好的連結。例如我想查詢 `$env:APPDATA` 應用程式資料的目錄，已經設定了多少連結，就可以用以下命令查詢：

```powershell
Get-ChildItem -Path "$env:APPDATA" -Recurse -Directory | Where-Object {$_.Attributes -band [System.IO.FileAttributes]::ReparsePoint} | Format-List FullName, Mode, Target, Attributes
```

> 附註: \[System.IO.FileAttributes\]::ReparsePoint 是 .NET 中 FileAttributes 列舉的一個成員，表示檔案或目錄包含重新分析點（Reparse Point）。重新分析點是一種與檔案或目錄關聯的使用者定義資料區塊，常用於實現符號連結（Symbolic Links）、目錄聯結點（Junction Points）和卷掛載點（Volume Mount Points）等功能。在 Windows、Linux 和 macOS 上，ReparsePoint 屬性均受支援。

輸出結果如下：

```
FullName   : C:\Users\user\AppData\Roaming\espanso
Mode       : l----
Target     : C:\Users\user\Dropbox\Tools\espanso
Attributes : Directory, ReparsePoint

...
```

如果想查詢我的文件夾，可以將指令改成這樣：

```powershell
Get-ChildItem -Path "$env:USERPROFILE\Documents" -Recurse -Directory | Where-Object {$_.Attributes -band [System.IO.FileAttributes]::ReparsePoint} | Format-List FullName, Mode, Target, Attributes
```

這樣就可以查詢 `$env:USERPROFILE\Documents` 文件夾下的所有資料夾是否有設定連結了：

```
FullName   : C:\Users\user\Documents\LINQPad Plugins
Mode       : l----
Target     : C:\Users\user\Dropbox\Tools\LINQPad\LINQPad Plugins
Attributes : Directory, ReparsePoint

FullName   : C:\Users\user\Documents\LINQPad Queries
Mode       : l----
Target     : C:\Users\user\Dropbox\Tools\LINQPad\LINQPad Queries
Attributes : Directory, ReparsePoint

FullName   : C:\Users\user\Documents\LINQPad Snippets
Mode       : l----
Target     : C:\Users\user\Dropbox\Tools\LINQPad\LINQPad Snippets
Attributes : Directory, ReparsePoint
```

### 相關連結

-   [What is the difference between a hard link and a symbolic link?](https://www.linkedin.com/pulse/what-difference-between-hard-link-symbolic-besmira-a-/)
-   [What is the difference between a symbolic link and a hard link? - Stack Overflow](https://stackoverflow.com/q/185899/910074)
-   [hardlink - What is the difference between NTFS hard links and directory junctions? - Super User](https://superuser.com/q/67870/95798)
-   [Hard links and junctions - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions?WT.mc_id=DT-MVP-4015686)
