そろそろ本気でコマンドライン③ : ファイル

Linux/UNIX のファイルは Windows などとは少し違う性質がある。そこで今回は、ファイルシステムの構造と絡めて少し説明してみようと思う。

ファイルの名前と i-node

Linux/UNIX のファイルは i-node と言う番号で管理されている。これは、物理/論理デバイス内で付与されるユニークな番号で、通常は意識する事が無い。

格納されているデバイスは、df コマンドなどで見ることが出来る。df コマンドの詳細は「man df」で確認して下さい。

df コマンド

つまり、ファイル本体は i-node 番号と考える事も出来る。そしてファイル名は、i-node 番号と結びついているだけだ。

では、ファイル名はドコで管理されているのだろうか?

その答えはディレクトリファイルと言う特殊なファイルだ。この性質を利用して、1つのファイルが複数のファイル名を持つ事が出来る。

Linux/UNIX のファイル名

この例では、「inode-1」に対して、「ファイル名-1」、「ファイル名-2」、「ファイル名-3」のファイル名が設定されている。

これを行うには、「ln」コマンドを使用する。

$ ln 元ファイル 新たな名前

こうやって作成されたモノをハードリンクと呼びます。

また、このハードリンク数は、「ls -l」や「stat」コマンドを用いて確認が行えます。

$ stat disable-intellipark.pid
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」で確認して下さい。
※ ハードリンクはその都合上、同一デバイス内でのみ作成が可能です。

では、どのタイミングでファイルはファイルは消えるのだろうか? それは、以下の条件を満たした場合である。

  1. ファイルが、全ての名前を失った時。
  2. ファイルが、プロセス(実行中のプログラム)によってオープンされていない時。

ここで注意してもらいたいのは、「2」の条件時である。

あるプロセスがファイルをオープンしている場合、そのファイルを「rm」や「unlink」コマンドで消した場合、ファイルを消した後でも、問題無くプロセスからの読み書きが行える。

何故なら「rm」や「unlink」コマンドで消すのはファイル名であり、ファイル本体ではないからだ。

しかし、プロセスがファイルをクローズして「1」の条件を満たした場合、ファイルが占めていた場所(つまりファイル本体)は再配置可能ブロック(上書き出来る領域)になる。

もしもファイルの完全消去を行いたいなら、「shred」コマンドを利用するのが良いだろう。

※ 詳しくは「man shred」で確認して下さい。

この性質を利用して、プログラムのバージョンアップを行う方法がある。

ここでは、「ls」コマンドをアップデートする例で説明しよう。

# # ここで、新しいファイルを「/usr/bin/ls.new」として書き込む。
#
# # 「/usr/bin/ls」のファイル名を削除する。
# # 勿論、「/usr/bin/ls」が実行中であっても問題が無い。
# rm /usr/bin/ls
#
# # 新しいファイルのファイル名を変更する。
# mv /usr/bin/ls.new usr/bin/ls

実行中のプログラムは、カーネルによってオープンされているので、このような事が可能なのである。

mv コマンド

mv コマンドはファイルを移動するコマンドではない。厳密には、次の何れかを実行するコマンドである。

  1. 移動元と移動先が同一デバイスである場合は、移動先のファイル名の作成と、移動元のファイル名の削除。
  2. 移動元と移動先が別デバイスである場合は、移動先にコピーを作成し、移動元のファイル名の削除。

つまり、移動元と移動先が同一デバイスである場合は、ファイル名の移動のみなので、ファイルサイズに関係なく一瞬で終了する。

また、移動元と移動先が別デバイスである場合は、殆どコピーと変わらないので、ファイルサイズによって処理時間は変化するし、コピーが途中で失敗した場合は、元のファイルが残る事にもなる。

ファイル拡張子

Linux/UNIX にはファイル拡張子は不要だと言う意見がある。少し極端な話だが、正確には「必ずしも必要ではない」だ。その仕組みについて、少し説明してみよう。

Linux/UNIX には「file」コマンドが存在する。

仮に「a.png」と言うファイルの拡張子を変更して「a.jpg」にしたとしよう。ここで、「a.jpg」を「file」コマンドで調べてみる。

$ file a.jpg
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」とする。

#!/usr/bin/bash
echo “hellow!!”

次に実行権を付与する。

$ chmod +x exe1

実行する。

$ ./exe1
hellow!!

これは先頭行を読み込み、「/usr/bin/bash」を実行し、標準入力にファイルの内容を流し込んでいる。つまり、先頭行が bash スクリプトを表すヘッダである。

次に、先頭行を「#!/usr/bin/cat」に変更して実行してみよう。

どうなっただろうか? ファイルの内容が表示されたのが確認出来ただろう。

この仕組みが利用して、拡張子が無いスクリプトは起動する事が出来るのだ。ただしバイナリーの実行では、スクリプトが起動されるのではなく、ヘッダ内容によって対応するプログラムローダーが実行される事になる。

だがこの仕組みは、ファイルの内容が判別出来ることが前提になっている為、判別出来ない・判別しつらいモノには無効である。例えばプログラムのソースなどでは、明確な判定ポイントが存在しない場合が多く、拡張子は必須になる。また、magic ファイルに記述が無いモノも同様である。

※ 実際には magic ファイル以外にも「ヘッダ情報 OR 拡張子」→「Mimeタイプ」で判定する方法があり、GUI アプリケーションではその方法が多く用いられている。(参考

※ いくつかコマンドが、bash や Python で作られていますが、あえてスクリプトである事を示す為に「.sh」などの拡張子を付加したり、実行可能ファイルである事を示す為に「.run」を付加する場合もあります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です