Linux 講座 / ハードリンクとシンボリックリンクの理解 - 違いと作成実習

Linuxのハードリンクは既存のファイルと同じinodeを指すもう一つの名前で、シンボリックリンクは別のパスを書き留めた別のファイルです。どちらの方法も異なる名前でファイルにアクセスできるようにしますが、元の名前を変更したりファイルシステムの境界を越えると結果が異なります。この講座では、実習ファイルを作成して違いを直接確認します。

inodeとリンク数の意味が馴染みがない場合は、file·statでファイル属性を読むの出力説明も併せて見てください。

リンクを理解するのに必要な二つの概念

ディレクトリに表示されるreport.txtのような名前は、ファイルを探すパスの一部です。ファイルシステムはファイルのメタデータをinodeで管理します。ハードリンクを追加すると、同じinodeを指す名前がもう一つできます。したがって、二つの名前のうち一つを削除しても、もう一つの名前が残っていればファイルにアクセスすることができます。

シンボリックリンクは対象のパス文字列を持つ別項目です。対象を開くとき、そのパスに従います。リンクが指す名前がなくなったり位置が変わると、リンクは残っていても対象にアクセスできないことがあります。これを切れたリンクと言います。

比較基準 ハードリンク シンボリックリンク
対象となるもの 同じファイルのinode 対象パス文字列
inode 元の名前と同じ リンク自体のinodeが別にある
元の名前を削除・変更 別の名前で引き続きアクセス可能 記録されたパスが消えると切れる可能性がある
別のファイルシステム 境界を越えることはできません 他のファイルシステムのパスを指すことができます
ディレクトリリンク 通常作ることはできません 作ることができます

練習用ファイルと二つのリンクを作る

練習はホームディレクトリ内の新しいフォルダで行います。~/linux-course-09-practiceがすでにある場合は名前を変えて新しいフォルダを作ってください。既存のフォルダがあると以下のmkdirは失敗します。その状態で後のコマンドを続けて実行すると既存のファイルを上書きする可能性があるので、必ず中止してください。

mkdir ~/linux-course-09-practice

cd ~/linux-course-09-practice

pwd

pwdが先ほど作った練習フォルダを指していることを確認してからのみファイルを作ってください。下記のprintfはoriginal.txtに一行を書き込みます。

printf 'version 1\n' > original.txt

ln original.txt hard.txt

ln -s original.txt shortcut.txt

最初の ln は基本動作であるハードリンクを作成し、-s を付けた二番目のコマンドはシンボリックリンクを作成します。shortcut.txt には original.txt という相対パスが記録されます。このパスは現在のシェルの位置ではなく、リンクが置かれたディレクトリを基準に解釈されます。

inode とリンク対象の確認

GNU stat で inode 番号(%i)とハードリンク数(%h)を確認してみてください。readlink はシンボリックリンクに記録されたパスを表示します。

stat -c '%i %h %n' original.txt hard.txt shortcut.txt

readlink shortcut.txt

inode の数字自体はシステムによって異なりますが、以下のように original.txt と hard.txt の inode 番号は同じで、リンク数は 2 と表示されます。shortcut.txt は別の inode を使用します。

123456 2 original.txt

123456 2 hard.txt

789012 1 shortcut.txt

original.txt

例の数字は説明用です。ファイルシステムや環境によって実際の値は異なります。readlink が出力した相対パス original.txt はリンクがあるフォルダ内で解釈されるため、3 つの名前すべてが現在は同じ内容を読むことができます。

cat original.txt

cat hard.txt

cat shortcut.txt

各コマンドはversion 1を出力します。しかし、同じ内容を表示しているからといって、3つの名前の構造が同一であるわけではありません。

元の名前を変更するとどうなるでしょうか?

実習フォルダ内でoriginal.txtの名前を変更してみてください。hard.txtは同じinodeを指すためそのまま読み取れます。一方でshortcut.txtはまだ以前のパスoriginal.txtを指しており、途切れてしまいます。

mv original.txt renamed.txt

cat hard.txt

readlink shortcut.txt

cat hard.txtはversion 1を出力し、readlink shortcut.txtは依然としてoriginal.txtを出力します。この状態でcat shortcut.txtを実行すると、対象がないというエラーが出るのが予想される結果です。同じフォルダに新しい名前を指すリンクが必要であれば、ln -s renamed.txt new-shortcut.txtのように新しいリンクを作成できます。

シンボリックリンクを削除する際、rm shortcut.txtは通常リンク自体を削除し、対象ファイルは削除しません。ただし、パスの最後にスラッシュを付けたり、リンクが指すディレクトリ内のファイルを対象にコマンドを実行すると意味が変わる可能性があるため、削除する前に対象パスを確認してください。

どのリンクを選びますか?

同じファイルシステム内で既存のファイルにもう一つの名前を付け、どちらかの名前が消えても他の名前でアクセスし続けたい場合はハードリンクが適しています。別のディレクトリやファイルシステムのパスやディレクトリを指し、リンクがどこを指しているかをパスで示したい場合は、シンボリックリンクの方が柔軟です。シンボリックリンクを移動したり、対象の名前を変更する予定がある場合は、相対パスがリンクの位置を基準に解釈される点を事前に確認する必要があります。

lnのオプションとリンク作成時の宛先の解釈はlnコマンド詳細説明、リンクに保存されたパスと解釈結果はreadlinkコマンド詳細説明でさらに確認できます。

比較後に元の名前に戻すには、同じ実習フォルダで以下を実行します。その間に新しいoriginal.txtを別に作成した場合は、上書きせずにまず確認してください。追加で作成したnew-shortcut.txtはまだrenamed.txtを指しているため、別途整理するか、再作成する必要があります。

mv -i renamed.txt original.txt
cat shortcut.txt
stat -c '%i %h %n' original.txt hard.txt

これで、shortcut.txtでversion 1を読むことができ、両方のハードリンクのinodeは引き続き同じです。

公式ドキュメント

ハードリンクとシンボリックリンクの動作はGNU Coreutilsのlnドキュメントを、readlinkの出力方式はGNU readlinkドキュメントを参考にしました。