Linux で作業していると,次のような場面があります。
- データの実体は大容量ストレージに置きたい
- でも,毎回
/mnt/hdd1/username/project/dataのような長いパスを書くのは面倒 - Python スクリプトでは
~/dataのように簡潔に参照したい - 複数のサーバーやPCで,同じコードをできるだけそのまま動かしたい
- Git 管理しているコードに,環境依存の絶対パスを書きたくない
このようなときに便利なのが symbolic link(シンボリックリンク) です。
まず,やりたいことは以下のようなイメージです。
実体: /mnt/hdd1/username/project/data
見せ方: ~/data
つまり,データの実体は /mnt/hdd1/... のような場所に置いたまま,作業時には ~/data という短いパスでアクセスできるようにします。
~/data
↓ symbolic link
/mnt/hdd1/username/project/data
symbolic link とは
シンボリックリンクは,簡単に言うと
別の場所にあるファイルやディレクトリへのショートカット
です。
例えば,実体が以下にあるとします。
/mnt/hdd1/username/project/data
これに対して,ホームディレクトリに ~/data というリンクを作れます。
ln -s /mnt/hdd1/username/project/data ~/data
すると,以下の2つのパスから同じデータにアクセスできます。
/mnt/hdd1/username/project/data
~/data
ただし,データが2か所にコピーされているわけではありません。
実体はあくまで,
/mnt/hdd1/username/project/data
にあります。
~/data は,そこを指しているだけです。
基本コマンド
シンボリックリンクは ln -s で作ります。
ln -s 実体のパス リンク名
例:
ln -s /mnt/hdd1/username/project/data ~/data
確認するには,
ls -l ~
を実行します。
以下のように -> が表示されていれば,シンボリックリンクです。
data -> /mnt/hdd1/username/project/data
リンク先の実体パスを確認したい場合は,
readlink -f ~/data
を使えます。
出力例:
/mnt/hdd1/username/project/data
何が嬉しいのか
1. パスを短くできる
例えば,毎回以下のようなパスを書くのは面倒です。
/mnt/hdd1/username/project/data
シンボリックリンクを作れば,
~/data
として扱えます。
Python でも,
from pathlib import Path
data_root = Path("~/data").expanduser()
のように書けます。
2. 容量の大きい場所に実体を置ける
/home の容量が小さく,/mnt/hdd1 や /mnt/ssd1 のような別ストレージの容量が大きいことがあります。
その場合,大きなデータを ~/data に直接置くと,ホームディレクトリ側の容量を圧迫してしまいます。
そこで,実体は大容量ストレージに置きます。
/mnt/hdd1/username/project/data
そして,ホームディレクトリにはリンクだけを置きます。
~/data -> /mnt/hdd1/username/project/data
この場合,~/data として見えていても,実際に容量を使っているのは /mnt/hdd1 側です。
3. コード内のパスを環境ごとに変えなくてよい
複数の環境で同じコードを動かす場合,データの実体パスが環境ごとに違うことがあります。
例えば,
環境A: /mnt/hdd1/username/project/data
環境B: /mnt/ssd1/username/project/data
環境C: /data/username/project/data
のような場合です。
このとき,コード内に直接
data_root = "/mnt/hdd1/username/project/data"
のように書いてしまうと,環境Bや環境Cでは動かなくなります。
そこで,各環境で ~/data という見え方に統一します。
環境Aでは,
ln -s /mnt/hdd1/username/project/data ~/data
環境Bでは,
ln -s /mnt/ssd1/username/project/data ~/data
環境Cでは,
ln -s /data/username/project/data ~/data
としておきます。
すると,Python 側では常に
from pathlib import Path
data_root = Path("~/data").expanduser()
と書けます。
つまり,環境ごとの差分をシンボリックリンク側で吸収できます。
4. Git 管理しているコードに環境依存のパスを書かなくて済む
Git でコードを管理している場合,コード中にサーバー固有のパスを書くと扱いづらくなります。
例えば,
data_root = "/mnt/hdd1/username/project/data"
と書いて commit してしまうと,他の環境ではそのまま動かない可能性があります。
一方で,
data_root = Path("~/data").expanduser()
のようにしておけば,各環境で ~/data のリンク先だけ設定すれば済みます。
これは,複数サーバー・複数PC・チーム開発・再現実験などで便利です。
git clone するときの考え方
大きなファイルを扱うプロジェクトでは,リポジトリの実体も大容量ストレージ側に置くのがおすすめです。
例えば,
cd /mnt/hdd1/username
git clone https://github.com/xxx/yyy.git
その後,ホームディレクトリから入りやすいようにリンクを作ります。
ln -s /mnt/hdd1/username/yyy ~/yyy
すると,
cd ~/yyy
でプロジェクトに入れますが,実体は以下にあります。
/mnt/hdd1/username/yyy
この運用にすると,後から増える以下のようなファイルも大容量ストレージ側に保存されます。
data/
outputs/
checkpoints/
logs/
results/
Python から参照する場合
Python からも,シンボリックリンクは基本的に普通のディレクトリとして扱えます。
from pathlib import Path
data_root = Path("~/data").expanduser()
print(data_root.exists())
print(data_root.resolve())
exists() はパスが存在するかを確認します。
resolve() は,シンボリックリンクの先にある実体パスを表示します。
例えば,
~/data -> /mnt/hdd1/username/project/data
というリンクがある場合,
print(data_root)
print(data_root.resolve())
の出力は次のようになります。
/home/username/data
/mnt/hdd1/username/project/data
注意点
~ は Python で自動展開されないことがある
Python では,
path = "~/data"
と文字列で書いただけでは,ライブラリによっては ~ がホームディレクトリとして解釈されない場合があります。
そのため,基本的には以下のように書くのが安全です。
from pathlib import Path
path = Path("~/data").expanduser()
または,
import os
path = os.path.expanduser("~/data")
を使います。
リンク先を消すと壊れる
シンボリックリンクは,あくまでリンク先を指しているだけです。
そのため,リンク先の実体を削除すると,リンクは壊れます。
例えば,
~/data -> /mnt/hdd1/username/project/data
という状態で,
rm -r /mnt/hdd1/username/project/data
を実行すると,~/data は存在しているように見えても,中身にはアクセスできません。
この状態を broken symbolic link と呼びます。
リンクだけを削除したい場合
リンクだけを削除したい場合は,
rm ~/data
で削除できます。
この場合,リンク先の実体である
/mnt/hdd1/username/project/data
は削除されません。
ただし,リンク先の中身を削除する操作と混同しないように注意が必要です。
特に,rm -r や末尾の / を含む操作は慎重に行うべきです。
まとめ
シンボリックリンクは,
実体: /mnt/hdd1/username/project/data
見せ方: ~/data
のように,実際の保存場所と,作業時に使うパスを分離する仕組みです。
これにより,
- 長いパスを短くできる
- 大容量データを適切なストレージに置ける
-
/homeの容量を圧迫しにくい - Python スクリプト内のパスを簡潔に書ける
- 複数環境でパス表記を統一できる
- Git 管理しているコードに環境依存の絶対パスを書かずに済む
といったメリットがあります。
特に,データセット・学習済みモデル・実験結果・ログなど,サイズが大きくなりがちなファイルを扱う場合には便利です。
一言で言うと,
シンボリックリンクは,保存場所の都合を隠して,作業上のパスをきれいに見せるための仕組み
です。