symbolic link(シンボリックリンク)とハードリンク - ファイル参照の仕組み

symbolic link(シンボリックリンク)とハードリンク - ファイル参照の仕組み

結論:シンボリックリンクとハードリンクの違い

シンボリックリンクはパスへの参照(ショートカット)、ハードリンクは inode への追加参照(同一ファイルの別名)。迷ったらシンボリックリンクを使う。

使い分けの基準

  • 別ファイルシステム間・ディレクトリへの参照 → シンボリックリンク
  • 同一パーティション内でファイル削除後もデータを保持したいバックアップ用途 → ハードリンク
  • どちらか迷ったら → シンボリックリンク(ln -s

ln コマンドの基本構文

ln コマンド一本でシンボリックリンク・ハードリンクの両方を作成できる。

# シンボリックリンク(-s オプションが必須)
ln -s [対象ファイル/ディレクトリ] [リンク名]

# ハードリンク(-s なし)
ln [対象ファイル] [リンク名]

-s を忘れるとハードリンクが作成される。ディレクトリを対象にした場合はエラーになるため気付けるが、通常ファイルを対象にした場合は意図せずハードリンクが作られる。

シンボリックリンクはファイルシステム上に「パス文字列を記録した特殊ファイル」として存在する。Windows のショートカットに相当するが、透過的に扱われる点が異なる。

$ ln -s /etc/nginx/nginx.conf ./nginx.conf
$ ls -la
lrwxrwxrwx 1 user user 22 Jan  1 00:00 nginx.conf -> /etc/nginx/nginx.conf

ls -la 出力の先頭 l はシンボリックリンクを示す。-> の後ろに記録されたパスがリンク先。

シンボリックリンクの特性

特性 詳細
ファイルシステム跨ぎ 可(別マウントポイント間も OK)
ディレクトリへのリンク
リンク先削除後 リンクは残るが「壊れたリンク」になる
参照解決 アクセスのたびにパスを辿って解決

リンク先の確認

# リンク先のパスを表示
$ readlink nginx.conf
/etc/nginx/nginx.conf

# 絶対パスで解決して表示
$ readlink -f nginx.conf
/etc/nginx/nginx.conf

ハードリンクは同一の inode を指す追加のディレクトリエントリ。「同じファイルが 2 つの名前を持っている」状態であり、実体(inode とデータブロック)は 1 つ。

$ echo "hello" > original.txt
$ ln original.txt hardlink.txt
$ ls -lai
1234567 -rw-r--r-- 2 user user 6 Jan  1 00:00 hardlink.txt
1234567 -rw-r--r-- 2 user user 6 Jan  1 00:00 original.txt

先頭の数値(1234567)が inode 番号。同じ番号 = 同一の実体。リンクカウント(-rw-r--r-- 22)がどちらのエントリからも同じ inode を参照していることを示す。

ハードリンクの特性

特性 詳細
ファイルシステム跨ぎ 不可(同一パーティション内のみ)
ディレクトリへのリンク 不可(通常ユーザーは作成できない)
元ファイル削除後 データは存続(参照カウントが 0 になるまで削除されない)
参照解決 inode 直接参照(パス解決なし)

シンボリックリンクとハードリンクの違いまとめ

両者の本質的な違いは「何を参照するか」にある。

項目 シンボリックリンク ハードリンク
参照先 ファイルパス(文字列) inode
FS 跨ぎ 不可
ディレクトリ対象 不可
元削除後 壊れたリンク(dangling link) データ存続
ls の権限表示 先頭が l 通常ファイルと同一
inode リンクファイル自身の inode を持つ 対象と同一 inode

実務での使い分け

シンボリックリンクが適しているケース

dotfiles 管理(設定ファイルのリポジトリ管理)

$ ln -s ~/dotfiles/.bashrc ~/.bashrc
$ ln -s ~/dotfiles/.vimrc ~/.vimrc

バージョン管理(current ディレクトリパターン)

# /opt/app-1.2.0 を current としてリンク
$ ln -s /opt/app-1.2.0 /opt/app/current

# バージョンアップ時はリンクを張り替えるだけ
$ ln -sfn /opt/app-1.3.0 /opt/app/current

-f で既存リンクを上書き、-n でリンク先がディレクトリの場合に内部ではなくリンク自体を置換する。

ハードリンクが適しているケース

増分バックアップ(rsync の --link-dest)

$ rsync -a --link-dest=/backup/prev/ /data/ /backup/today/

変更のないファイルはハードリンクで参照するため、ディスク消費を最小化しながら「各日付のスナップショット」を保持できる。

よくある落とし穴

相対パスのシンボリックリンクは場所によって壊れる

# /home/user で作成
$ ln -s ../etc/nginx/nginx.conf nginx.conf

# /tmp に移動すると壊れる
$ cd /tmp
$ cat nginx.conf
cat: nginx.conf: No such file or directory

リンクに記録されるのは指定したパス文字列そのもの。作成時のカレントディレクトリは関係しない。移動しても機能するリンクには絶対パスを使う

リンク先がディレクトリのシンボリックリンクを張り替える

# 誤: -f だけでは current がディレクトリの場合に内部にリンクが作成される
$ ln -sf /opt/app-2.0.0 /opt/app/current

# 正: -n を加えてリンク自体を置換する
$ ln -sfn /opt/app-2.0.0 /opt/app/current

壊れたシンボリックリンクの検出

$ find /path -maxdepth 1 -xtype l

-xtype l は「リンク先が存在しないシンボリックリンク」を検出するオプション(-type l だとリンク自体の型を見るため壊れたリンクも含んでしまう)。

次に読む