Windows ファイルパスを自在に操る ～複数パス、PC間でデータを同期、subst、シンボリックリンクなど～

この記事は [NITMic Advent Calendar 2023](https://adventar.org/calendars/8701) 5日目の記事です。

## はじめに
複数の場所に同じファイルやフォルダを置きたいとき、`mklink`コマンドや`subst`コマンドを使う方法があります。一つのファイルを2つ以上の場所から参照させる(ファイル/フォルダに複数のパスを与える)ことができ、ファイルをコピーするよりも容量の節約になり、同じ変更を複数のファイルに適用する必要もなくなります。また、GoogleDrive等クラウドストレージを使用して各種データをPC間で同期することが容易になります。エロゲのセーブデータとかね。
mklinkコマンドでは**シンボリックリンク**、**ハードリンク**、**ジャンクション**を作成でき、substコマンドでは仮想ドライブを作成できます。それぞれの違いを詳しく見ていきます。

| \ | ハードリンク | ジャンクション | シンボリックリンク | substコマンド |
|:-:|:-:|:-:|:-:|:-:|
|  使用可能なOS |  Windows 2000以降 | Windows 2000以降  |  Windows Vista以降 | Windows 3.1以降 |
|  リンク対象 | ファイル |  フォルダ |  両方 |  フォルダ |
|   |   |   |   |   |

:::note warn
subst以外はNTFSの機能であるため、DOSベースのWindows(9x系)では使用できません。WindowsMeをお使いの読者の皆様は、泣いて良い。
:::

なお、本記事ではフォルダ/ディレクトリの表記を「フォルダ」で統一しています。(ディレクトリという言葉が通じる人は多分この記事が必要ない)

## コマンドの実行の仕方
- `Windowsキー`+ `Rキー`で、`ファイル名を指定して実行`に`cmd`と入力してEnterキーを押すと起動します。
    - 管理者権限が必要な場合は`Ctrl`+`Shift`+`Enter`
    - cdコマンドを使って、フォルダを移動することができます。
- いろいろ細工すると、エクスプローラーで目的のフォルダを右クリックして`ここでコマンドプロンプトを起動`みたいなボタンを追加できます。是非調べてみてください。

## substコマンド
特定のフォルダを仮想ドライブとして割り当てるコマンドです。MS-DOS時代からあるコマンドですが、意外とこの手の記事に出てきません。
`subst H: C:\Users\xxx\Documents`とすると、`C:\Users\xxx\Documents`というフォルダの中身が**Hドライブ**として表示できるようになります。
### 仮想ドライブの割当て
```bat
subst [ドライブレター] [対象フォルダ]
```
- [ドライブレター]には、仮想ドライブとして設定したいドライブ文字を書きます。
  - 例えば、Hドライブを作成したい場合、`H:`のように指定します。
- [対象フォルダ]には、仮想ドライブの中身にしたいフォルダを指定します。
  - 例えば、`C:\Users\xxx\Documents`というフォルダの中身を仮想ドライブにしたい場合、そのまま記入します。
  - 相対パスや環境変数の使用も可能です。
    - 例えば、コマンドを`C:\Users\xxx\Documents\`で実行した場合、以下のような利用の仕方ができます。
      - そのフォルダを対象にする場合は`%CD%`
      - そのフォルダの中の`hoge`というフォルダを対象にする場合は`hoge\`
- コマンドプロンプトは必ず管理者権限なしで普通に起動しましょう
    - 普段エクスプローラーは管理者権限を持っておらず、管理者権限でsubstしたドライブが見えません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611114/a724673f-754b-f56c-75ab-b5b597e52b93.png)
こんなこともできちゃう(ドライブの空き容量表示は元のドライブと同一になります)

### 仮想ドライブの割り当て解除
```bat
subst [ドライブレター] /d
```
コマンドを打たずとも、パソコンをシャットダウンすると割り当て解除されます。

筆者は昔、異なるPCでUSBメモリのドライブ文字を統一するため、`subst X: %CD%`と書かれたバッチファイルをUSBメモリのルートディレクトリ(一番上の階層)に入れていました。こうすることで、USBメモリの中に入れたソフトウェアを使う上で都合が良くなります(絶対パスを使用するアプリケーション等)。Autorun.infが動いてた時代は自動で起動させていました。

## シンボリックリンク
フォルダー及びファイルのリンクを作成します。
例えば、`C:\Users\xxx\`で、`mklink /D Documents "C:\Documents"`を実行した場合、`C:\Documents\`の中身が`C:\Users\xxx\Documents\`の中身としても扱えるようになります。

### リンクの作成
```bat
mklink [/D] [リンク名] [リンク先]
```
- フォルダをリンクする場合は`/D`を書きます。ファイルのリンクを作成する場合は不要です。
- `[リンク名]` は、作成するリンクの名前を指定します。元のフォルダと同じで良い。
- `[元ファイルの場所]` は、作成するリンクが参照するファイル/フォルダを指定します。**相対パスや環境変数が使えます。**


- 元ファイル/フォルダが消された場合、リンクだけが残り、ファイルにアクセスできなくなります。
- コマンドの実行には管理者権限が必要です。

## ジャンクション
フォルダーのリンクを作成します。
```bat
mklink /J [リンク名] [リンク先のフォルダ名]
```
- `/J` を指定するとジャンクションを作成できます。
- **相対パス**は使えません
    - (のはずですが、当方のWin11環境では何故か使えました)
- コマンドの実行に管理者権限は必要ありません。
- その他の主な仕様はシンボリックリンクと同一です。

## ハードリンク
ファイルのリンクを作成します。
```bat
mklink /H [リンク名] [リンク先のファイル名]
```
- `/H` を指定するとハードリンクを作成できます。
- 上の2つと違い、元ファイルに対して複数のハードリンクを作成した場合、元のファイルを含む全てのリンクが消えるまでファイルの実体が残ります。
    - 言い換えれば、一つでもハードリンクが残っていれば、そのリンクからファイルにアクセスできます。

- コマンドの実行に管理者権限は必要ありません。

## シンボリックリンクとジャンクションの細かな違い
Windowsの機能としてある「保存」「開く」ダイアログ等(下のスクショの画面)でシンボリックリンクを開くと、パスが元ファイルのものに変わってしまいます(各ソフトウェアでファイルを読み込むとき、元ファイルのパスでしか読み込めなくなってしまいます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611114/e760666a-6ffa-71db-5857-7000be10fb03.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611114/3f0cadce-b970-1da7-67e3-345d5f5cbc1d.png)
    - ジャンクションではこのような現象は発生しませんでした。

技術仕様の違いに関してはこちらのページに詳しく書かれています。

https://adsaria.hatenadiary.org/entry/20100712/1278930723

## リンクをクラウドで使いたい
OneDrive/Googleドライブを使用してPC間のデータ同期をする場合、ファイルの実体をクラウド用フォルダに置き、本来の保存場所(ゲームのセーブデータ保存場所など)にシンボリックリンク及びジャンクションを貼る、という使い方をします。

:::note info
Dropboxに関しては未検証です。そのうち追記します。
:::

### 実体をクラウドのフォルダに置く
例えばゲームのセーブデータを同期したいとします。
OneDriveやGoogleDriveのデスクトップ版を導入してあり、セーブデータの場所が`C:\Users\xxx\Documents\Game\`にあるとします。
- セーブデータを保存しているフォルダのパスをコピーして控えておきます。
    - (今回であればDocumentsの中の`Game`フォルダ)
- `Game`フォルダをクラウドのフォルダに移動させ、そのパスもコピーして控えておきます。
    - Googleドライブの「マイドライブ」の直下に移動した場合、`G:\マイドライブ\Game`とかか。
- 任意の場所でコマンドプロンプトを起動し、控えたパスをコピペして以下のコマンドを入力
```bat
mklink /D [元の場所のパス] [ドライブ内のパス]
```
- 今回なら`mklink /D "C:\Users\xxx\Documents\Game\" "G:\マイドライブ\Game\"といった感じになると思います。
- 同期したいデータによってコマンドを書き換えることで、ブラウザのデータ、あるいはドキュメントフォルダをまるごと同期することもできます。
:::note info
「パスのコピー」は、ファイル及びフォルダをShiftキーを押しながら右クリックして、メニューから「パスのコピー」を選択することで簡単に行えます。
:::
:::note info
ゲームのセーブデータやブラウザのデータ等は、ユーザーフォルダ内の`AppData\Roaming`にあることが多いです。
このフォルダは、`ファイル名を指定して実行`かエクスプローラーのアドレス欄で`%appdata%`と入力すると簡単に開けます。
:::


### リンクをクラウドのフォルダに置いた場合
- OneDriveの「バックアップ」機能は、対象フォルダの中にシンボリックリンクやジャンクションが含まれていると、同期が行われません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611114/fb119f59-f8f3-d7a6-67aa-b9ed9ec4b731.png)

- OneDriveフォルダの中にシンボリックリンク及びジャンクションを作成すると、作成時点での状態のフォルダが同期されますが、その後の変更が同期されません。

- GoogleDrive(デスクトップ版)の「マイドライブ」内にはシンボリックリンクやジャンクションを作成できません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611114/ea3b9aa2-0e02-5da6-f3ad-9c1c387bfa7c.png)
    - GDriveの実装の問題と思われる
　
- OneDriveにハードリンクを貼ると同期がされなくなります。
    - これは同期の挙動の問題によるものと思われます。以下引用
>同期先の OneDrive 中の settings.json は File ID が新しいものに変化する。
>つまり、OneDrive (や Dropbox) は同期先のファイルを変更する時、既存のファイルに上書きするのではなく、一旦古いファイルを削除し、新しいファイルを作成するような挙動を行う。

https://qiita.com/yabeenico/items/11b8fc34f7e554c2c665

## まとめ
これで、ドキュメントフォルダからエロゲのセーブデータまで全てを同期することができますね！(クラウドの容量次第)