そろそろ本気でコマンドライン③ : ファイル
Linux/UNIX のファイルは Windows などとは少し違う性質がある。そこで今回は、ファイルシステムの構造と絡めて少し説明してみようと思う。
ファイルの名前と i-node
Linux/UNIX のファイルは i-node と言う番号で管理されている。これは、物理/論理デバイス内で付与されるユニークな番号で、通常は意識する事が無い。
格納されているデバイスは、df コマンドなどで見ることが出来る。df コマンドの詳細は「man df」で確認して下さい。
つまり、ファイル本体は i-node 番号と考える事も出来る。そしてファイル名は、i-node 番号と結びついているだけだ。
では、ファイル名はドコで管理されているのだろうか?
その答えはディレクトリファイルと言う特殊なファイルだ。この性質を利用して、1つのファイルが複数のファイル名を持つ事が出来る。
この例では、「inode-1」に対して、「ファイル名-1」、「ファイル名-2」、「ファイル名-3」のファイル名が設定されている。
これを行うには、「ln」コマンドを使用する。
こうやって作成されたモノをハードリンクと呼びます。
また、このハードリンク数は、「ls -l」や「stat」コマンドを用いて確認が行えます。
File: `disable-intellipark.pid’
Size: 4 Blocks: 8 IO Block: 4096 通常ファイル
Device: 23h/35d Inode: 17961 Links: 1
Access: (0644/-rw-r–r–) Uid: ( 0/ root) Gid: ( 0/ root)
Context: system_u:object_r:init_tmp_t:s0
Access: 2014-11-14 13:20:04.093774955 +0900
Modify: 2014-11-14 13:20:04.093774955 +0900
Change: 2014-11-14 13:20:04.093774955 +0900
Birth: –
※ ln コマンドの使い方は「man ln」で確認して下さい。
※ ハードリンクはその都合上、同一デバイス内でのみ作成が可能です。
では、どのタイミングでファイルはファイルは消えるのだろうか? それは、以下の条件を満たした場合である。
- ファイルが、全ての名前を失った時。
- ファイルが、プロセス(実行中のプログラム)によってオープンされていない時。
ここで注意してもらいたいのは、「2」の条件時である。
あるプロセスがファイルをオープンしている場合、そのファイルを「rm」や「unlink」コマンドで消した場合、ファイルを消した後でも、問題無くプロセスからの読み書きが行える。
何故なら「rm」や「unlink」コマンドで消すのはファイル名であり、ファイル本体ではないからだ。
しかし、プロセスがファイルをクローズして「1」の条件を満たした場合、ファイルが占めていた場所(つまりファイル本体)は再配置可能ブロック(上書き出来る領域)になる。
もしもファイルの完全消去を行いたいなら、「shred」コマンドを利用するのが良いだろう。
※ 詳しくは「man shred」で確認して下さい。
この性質を利用して、プログラムのバージョンアップを行う方法がある。
ここでは、「ls」コマンドをアップデートする例で説明しよう。
#
# # 「/usr/bin/ls」のファイル名を削除する。
# # 勿論、「/usr/bin/ls」が実行中であっても問題が無い。
# rm /usr/bin/ls
#
# # 新しいファイルのファイル名を変更する。
# mv /usr/bin/ls.new usr/bin/ls
実行中のプログラムは、カーネルによってオープンされているので、このような事が可能なのである。
mv コマンド
mv コマンドはファイルを移動するコマンドではない。厳密には、次の何れかを実行するコマンドである。
- 移動元と移動先が同一デバイスである場合は、移動先のファイル名の作成と、移動元のファイル名の削除。
- 移動元と移動先が別デバイスである場合は、移動先にコピーを作成し、移動元のファイル名の削除。
つまり、移動元と移動先が同一デバイスである場合は、ファイル名の移動のみなので、ファイルサイズに関係なく一瞬で終了する。
また、移動元と移動先が別デバイスである場合は、殆どコピーと変わらないので、ファイルサイズによって処理時間は変化するし、コピーが途中で失敗した場合は、元のファイルが残る事にもなる。
ファイル拡張子
Linux/UNIX にはファイル拡張子は不要だと言う意見がある。少し極端な話だが、正確には「必ずしも必要ではない」だ。その仕組みについて、少し説明してみよう。
Linux/UNIX には「file」コマンドが存在する。
仮に「a.png」と言うファイルの拡張子を変更して「a.jpg」にしたとしよう。ここで、「a.jpg」を「file」コマンドで調べてみる。
a.jpg: PNG image data, 466 x 295, 8-bit/color RGB, non-interlaced
すると、PNG ファイルであることが確認出来る。これは、「a.jpg」ファイルのヘッダ情報を確認し、結果を表示している。
これは、多くのファイルがヘッダ情報を有している事と、そのヘッダ情報を記述したファイル(magicファイル)を Linux/UNIX が所有している為に可能となっている。
※ ヘッダ情報を記述したファイルをに関する詳細は、「man magic」で確認して下さい。
この方法を利用したものに、実行可能ファイルの実行がある。通常、実行可能ファイルには拡張子は不要だ。勿論、ファイルの中身が 64bit バイナリだろうが、bsah や Python スクリプトでも同様である。何故なら、ファイルを実行するためにはファイルの読み込みが必要であり、そして先頭部分を調べてみてみれば、そのファイルが何であるかが判るからだ。
次のようなファイルを作成し、ファイル名を「exe1」とする。
echo “hellow!!”
次に実行権を付与する。
実行する。
hellow!!
これは先頭行を読み込み、「/usr/bin/bash」を実行し、標準入力にファイルの内容を流し込んでいる。つまり、先頭行が bash スクリプトを表すヘッダである。
次に、先頭行を「#!/usr/bin/cat」に変更して実行してみよう。
どうなっただろうか? ファイルの内容が表示されたのが確認出来ただろう。
この仕組みが利用して、拡張子が無いスクリプトは起動する事が出来るのだ。ただしバイナリーの実行では、スクリプトが起動されるのではなく、ヘッダ内容によって対応するプログラムローダーが実行される事になる。
だがこの仕組みは、ファイルの内容が判別出来ることが前提になっている為、判別出来ない・判別しつらいモノには無効である。例えばプログラムのソースなどでは、明確な判定ポイントが存在しない場合が多く、拡張子は必須になる。また、magic ファイルに記述が無いモノも同様である。
※ 実際には magic ファイル以外にも「ヘッダ情報 OR 拡張子」→「Mimeタイプ」で判定する方法があり、GUI アプリケーションではその方法が多く用いられている。(参考)
※ いくつかコマンドが、bash や Python で作られていますが、あえてスクリプトである事を示す為に「.sh」などの拡張子を付加したり、実行可能ファイルである事を示す為に「.run」を付加する場合もあります。