0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

symbolic link を使って,保存場所と見せ方を分離する

0
Posted at

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 管理しているコードに環境依存の絶対パスを書かずに済む

といったメリットがあります。

特に,データセット・学習済みモデル・実験結果・ログなど,サイズが大きくなりがちなファイルを扱う場合には便利です。

一言で言うと,

シンボリックリンクは,保存場所の都合を隠して,作業上のパスをきれいに見せるための仕組み

です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?