UNIX/linuxベースのOSにおいて、C標準ライブラリを使って、ファイルシステムにおける指定したディレクトリの情報を取得する方法をまとめます。
環境
m1 mac OS(11.6)
ファイルシステムにおけるディレクトリについて
ディレクトリはデータ領域にディレクトリに関する情報を保持しています。 ディレクトリに関する情報は、主に下記になります。
ディレクトリ配下のファイル情報
- ファイル名
- ファイル名に対応するiノード番号
iノードはここでは詳しく説明しませんが、ファイルシステムにおいて、ファイルの属性情報を管理するためのデータとなります。 そのiノードごとに番号が割り振られていて、この番号のことをiノード番号と呼びます。
ディレクトリ配下に存在するファイルごとに、それを特定するファイル名と、ファイル名に紐づいたiノード番号が対応づけられ、これによってファイル名と対応したファイルデータをiノード番号から取得できます。
ディレクトリストリームを使ったディレクトリ情報の取得
ここで、C言語にて、ファイルシステムのディレクトリ情報をとるための標準ライブラリ関数が用意されています。 C言語において、dirent.hをincludeすることで利用できます。
dirent.h(0p) - Linux manual page
ファイル操作でストリームを使うのと同様に、ディレクトリに対してもディレクトリストリームの仕組みが用意されていて、そのストリームをもとにディレクトリの操作をします。
ディレクトリの操作について
ディレクトリストリームを使ったディレクトリの操作は、主に以下の手順でおこないます。
- opendir(3)で指定したパスのディレクトリストリームを取得する
- readdir(3)で、1で取得したストリームから、ディレクトリ配下のファイル情報を取得する。 (readdir(3)は一度の呼び出しで1ファイルの情報を取得する。
- closedir(3)で、ディレクトリストリームを閉じる
ここで、2のreaddir(3)でディレクトリエントリ(ディレクトリ内の項目)の情報を読み取ります。 readdir(3)の戻り値の型はstruct direntで、POSIXで定義されているフィールドは下記のような構成になっています。
struct dirent { ino_t d_fileno; // iノード番号 char d_name[]; // ファイル名 }
ディレクトリストリームを使った簡単なlsを作る
ここで、ディレクトリストリームを使って、カレンとディレクトリに対するディレクトリエントリの情報を表示する簡易lsコマンドを実装してみます。
/** * ディレクトリストリームを使った簡単なls */ #include <errno.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <dirent.h> int main(void) { DIR *dirp; // ディレクトリストリーム struct dirent *p; // ディレクトリ項目用の構造体 readdirで取得する // カレントディレクトリのディレクトリストリームを取得 if ((dirp = opendir(".")) == NULL) { perror("opendir"); exit(1); } errno = 0; // ディレクトリ項目がなくなるまでreaddirする while((p = readdir(dirp)) != NULL) { // ファイル名とiノード番号を表示する。 printf("filename: %s, inode: %llu\n", p->d_name, p->d_ino); } if (errno != 0) { perror("readdir"); exit(1); } // ディレクトリストリームを閉じる if (closedir(dirp) != 0) { perror("closedir"); exit(1); } return 0; }
カレントディレクトリに対するストリームを開き、そのストリームに対して、ディレクトリエントリを全部たどりきるまでreaddirを実行して、ディレクトリエントリ(ファイル)の情報を取得します。 そこでファイル名:d_name、iノード番号:d_inoを表示させます。
上記のコードをコンパイルして実行すると、下記の結果となります。
./a.out filename: ., inode: 4323177 filename: .., inode: 81985 filename: ch06, inode: 6747507 filename: ch01, inode: 4378311 filename: test022, inode: 4743777 filename: LICENSE, inode: 4323246 filename: test, inode: 4743763 filename: ch04, inode: 4956826 filename: ch03, inode: 4616053 filename: README.md, inode: 4323247 filename: sub, inode: 4684166 filename: ch02, inode: 4516118 filename: a.out, inode: 6748530 filename: ch05, inode: 5625378 filename: foo.txt, inode: 4527223 filename: .gitignore, inode: 4323245 filename: test.txt, inode: 4743184 filename: phone1.dat, inode: 4706419 filename: .git, inode: 4323178 filename: .vscode, inode: 4392160