シンボリックリンクやハードリンクを理解するためには、ファイルやディレクトリの正体について理解すると良いと思い書いてみました。ターミナルで表示されるディレクトリやファイルを想像していただいて、お読みいただければと思います。
まず、分かりやすくするための前提を置きます。
- Unix(linux)のパソコンに1つのHDDがあります。
- HDDは1個しかないとします(HDDの中身はCDのような円盤の形のディスクです)。
- 物理的に1個しかない中に色々なデータが入ります。
我々は、HDDにいろいろな情報を日々ゴリゴリと書いていますが、円盤の形をしたディスクの上には、その情報を整理するためのタンスがあらかじめ用意されているわけではありません。CDのようなこのディスクに、日々書き加えられていくファイルは、画像であっても文章であっても、順番にディスクに記録されるにすぎないわけです。そう考えると、ターミナル上で"ls"コマンドを実行するとディレクトリやファイルが存在し、さらにそのディレクトリの中には、ファイルが置かれて整理されているように見えることがとても不思議です。例えば、HDDが複数あって物理的分かれている環境であれば、項目ごとにその物理領域を分ければ、綺麗に整理することができますね。しかし、前提に置いたHDDが一つしかない場合は、ファイルの整理をどのようにすればよいでしょうか。
やはり、人間にとっては、タンスのような箱に種類別のファイルを分けて整理して見えるようにしたほうが、ファイルを閲覧したり、プログラムを書いたりするのに便利だし分かりやすいはずです。この方法として、linuxのカーネル上では、ファイルの置く箱(ディレクトリ)を人間がイメージできる仕組みを用意しました。この仕組みは、物理的にHDDを区切るのではなく、データの置き場を記した新たなファイルを用意することで、既にディスクに記録してある目的の情報にアクセスするものです。さて、この記事の結論について先にまとめてしまいましょう。
そもそも、我々がlinuxを使っている中で扱うディレクトリはファイル(ディレクトリエントリ)であり、箱のようなもののなかに目的のファイルが入っているわけではない。
cdコマンドでディレクトリに移動したときに、実際は移動したのではなく、"ディレクトリにみえるファイル"であるディレクトリエントリにアクセスして、そのディレクトリに紐づけているファイル名を取り出し、HDDの中にあるさまざまな情報を得ることで、ディレクトリの中にあたかもファイルがあるようにみせている。
このときに、ディレクトリエントリには、HDDに書き込まれた情報に対してiノードとよばれる固有番号とファイル名が結びつけられて記載されている。
以上の3つが、ターミナル上で我々がみているディレクトリというものの正体です。さて、さっそくディレクトリエントリをみてみましょう。
ls -i1 ~/
Lizt-2:link_test suimye$ ls -i1 ~/
3823092 leksah
12353489 3end_ids.txt
2107521 4.0
7776827 ABBYY
1254196 Applications
1256637 Books
10959751 Creative Cloud Files
1256645 CytoscapeConfiguration
1258228 D4Z4.genome
1617443 DL
1258233 Desktop
1258458 Documents
1262012 Downloads
1268915 Dropbox
このように、ディレクトリやファイルという種類に限らず iノード番号と名前が一行に記載されています。この番号と名前をもとに、HDDの中の目的の情報を取り出すという操作をいろいろなコマンドで日々私たちは行っていることになります。iノード番号と名前のリストさえあれば目的の情報を引き出すことができます。この情報のリストの作り方が、直接か間接かでハードリンクとシンボリックリンクの違いが生まれます。
ハードリンク、シンボリックリンク
ハードリンク
まずハードリンクの貼り方を習得し、その中味を見てみましょう。
私のmacの中にtest.shというファイルが、ホーム直下にたまたまありましたのでこれで試してみます。まずこのtest.shファイルの中身をみてみましょう。
cat ~/test.sh
#!/bin/sh
TPATH="/Volumes/Macintosh\ HD/"
LS_PATH=`eval ls $TPATH`
echo $LS_PATH
適当なシェルスクリプトを書いた残骸のようです(笑)。このファイルのハードリンクを、link_testという元のファイルとは異なるディレクトリを作りその中に貼りたいと思います。ハードリンクは、別の名前でリンクを書くことができます。
##ディレクトリをつくり、移動
mkdir link_test
cd link_test
##ハードリンクをはる
ln ~/test.sh kaeru.sh
ls
##lsで確認してみる
Lizt-2:link_test suimye$ ls
kaeru.sh
Lizt-2:link_test suimye$
lnコマンドの結果、kaeru.shみたいなファイルができましたね?このファイルの中身をcatでみてみましょう。
Lizt-2:link_test suimye$ cat kaeru.sh
#!/bin/sh
TPATH="/Volumes/Macintosh\ HD/"
LS_PATH=`eval ls $TPATH`
echo $LS_PATH
さっきのtest.shと同じ内容になっていますね。これは、test.shをコピーしたわけではなく(cpコマンドを使ってませんよね)lnコマンドによって、ある特定のファイルのiノードを参照するディレクトリエントリが一つ追加されているのです。確認してみましょう。
ls -l1 kaeru.sh ~/test.sh
実行結果
Lizt-2:link_test suimye$ ls -i1 kaeru.sh ~/test.sh
17556255 /Users/suimye/test.sh
17556255 kaeru.sh
同じiノード番号になってますね!
もう一度繰り返しいいますと、大元のtest.shのデータが2つに増えているわけではなく、ディレクトリエントリと呼ばれるファイルが2つに増え、それぞれ同じiノードを参照して同じ情報にアクセスできる。ということです。
シンボリックリンク
シンボリックリンクも、すでにあるファイルに別名をつけて、 その名前で参照できるようにします(同一の名前でも大丈夫)。この点では実用的に全く同じに感じるかもしれません。しかし違いは、元のファイルをiノードではなくパスで指定するため、元のディレクトリエントリに対してリンクするディレクトリエントリを作成するイメージになります。
ln -s ~/test.sh kaeru2.sh
ls -l1 ~/test.sh kaeru2.sh
kaeru2.shがディレクトリ内にできていると思います。先ほどと同様に、iノード番号をみてみましょう。
Lizt-2:link_test suimye$ ls -i1 ~/test.sh kaeru2.sh
17556255 /Users/suimye/test.sh
18492941 kaeru2.sh
iノードが異なりますね。これは、test.shは17~番のiノードを介してHDDの情報をみに行きますが、kaeru2.shはtest.shへのiノード番号を介してtest.shへ、そして、test.shからHDDの情報をみることになるわけです。
## ls -lをするとリンクの関係を"->"という矢印を使って分かりやすく教えてくれます。
Lizt-2:link_test suimye$ ls -l kaeru2.sh
lrwxr-xr-x 1 suimye staff 21 6 18 00:11 kaeru2.sh -> /Users/suimye/test.sh
Lizt-2:link_test suimye$
つまり、「リンクのリンクのようなもの」と考えられそうです。
もちろん中身はハードリンクと同様にみることができます。
cat kaeru2.sh
#!/bin/sh
TPATH="/Volumes/Macintosh\ HD/"
LS_PATH=`eval ls $TPATH`
echo $LS_PATH
シンボリックリンクの場合は、リンクのリンクのようなもののため、test.shを消してしまうと、kaeru2.shどこに情報があるかわからない(ファイルが消えてしまう)ことになりますが、ハードリンクの場合、同じiノード番号を持つディレクトリエントリが2つなので、test.shを消してもkaeru.shがあれば情報をみることができるわけです。
このように、HDDに記録されていく情報は、iノード番号とよばれる番号で管理されています。その番号を効率よく使う方法や整理する方法として、ハードリンク、シンボリックリンク、ディレクトリエントリなどの方法が使われているわけです。