ディレクトリの基本操作

このエントリーをはてなブックマークに追加

まずは現在位置取得やディレクトリの移動といった基本的で簡単な操作から解説していきます。
C言語を勉強し始めて気づく問題点の1つがこのディレクトリ操作です。ANSIで定められている標準的なC言語の範囲ではディレクトリ操作が一切ないのです。
ファイル操作のfopen()等ではパスを指定することができますが、これはあくまでもファイルに対する操作であって、現在のディレクトリの位置やディレクトリにあるファイルのリストを取得するといった簡単な操作が一切出来ないのです。
つまり、こういった操作を行いたいのであれば、OS固有の機能を使う必要があるのです。(ヒドい話ですよね)
ディレクトリ操作は基本的にPOSIXで定義されていますので、安心してください。

現在のディレクトリ位置

さて、では早速プログラムを見て行きましょう。
ディレクトリ位置(パス)の取得にはgetcwd(3)を使用します。

#include <stdio.h>
#include <unistd.h>
#include <limits.h>

int main(void)
{
        char buf[PATH_MAX];

        getcwd(buf, sizeof(buf));
        printf("%s\n", buf);

        return 0;
}

非常に簡単なプログラムですので特に解説は必要ないでしょう。
と言いたいのですが、1つだけ解説させてください。

char buf[PATH_MAX];
問題はこのコードです。PATH_MAXという見慣れないコードがあります。これは一体なんでしょうか?

まず、bufにはディレクトリパスが入るので、ある程度余裕がある必要があります。
理由は簡単で、bufの長さが短すぎると当然メモリバッファローが発生します。
問題はbufの長さをどれだけ持つ必要があるか?です。ディレクトリは入れ子でいくらでも深くできますし、ディレクトリ名も自由です。
つまりbufの長さを確定出来ないことがわかります。1024等大きめに取得することは可能ですが、だからと言って安全性は保証できません。
ただし、Unixではパス名には長さの限界が定められています。それがlimits.hにあるPATH_MAXです。ですので、bufにはPATH_MAXを使えば問題無いというわけです。


ディレクトリ移動

次はディレクトリ移動です。これも簡単ですが必須の機能でしょう。
解説は不要でしょう。

#include <stdio.h>
#include <unistd.h>
#include <limits.h>

int main(void)
{
    char buf[PATH_MAX];
    
    getcwd(buf, sizeof(buf));
    printf("%s\n", buf);
    
    chdir("/tmp");
    
    getcwd(buf, sizeof(buf));
    printf("%s\n", buf);
    
    return 0
}


ディレクトリ作成

次はディレクトリ作成です。

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(void)
{
    mkdir("aaaa", 0755);
    
    return 0;
}

まず、mkdir(2)の宣言を見てみましょう。

#include <sys/stat.h>
#include <sys/types.h>

int mkdir(const char *pathname, mode_t mode);
初めて見て戸惑うのは"mode_t mode"の部分です。 manにはこのように書いていますが、意味不明でしょう。
mode  引き数は、作成されたディレクトリの許可属性を決定するのに使われる。  この値に、通常通りプロセスの  umask による修正が加えられる。 したがって、作成された
ディレクトリの許可属性は (mode & ~umask & 0777)   となる。  作成されたディレクトリのその他のモードビットはオペレーティングシステムに  依存する。Linux  の場合
は、以下の通りである。

新しく作成されたディレクトリの所有者はプロセスの実効ユーザ ID に設定される。 新たに作成されたディレクトリが含まれる親ディレクトリに set group ID ビットがセッ
トされていたり、ファイルシステムが BSD の グループセマンティクス (mount -o bsdgroups あるいは、同じ意味の mount -o  grpid)   に従ってマウントされている場合に
は、  新たに作成されたディレクトリのグループ所有権は親ディレクトリの ものが継承される (親ディレクトリと同じになる)。 それ以外の場合は、グループ所有権はプロセ
スの実効グループ ID となる。

もし親ディレクトリに set group ID ビットがセットされていれば新しく作成される ディレクトリにも set group ID ビットがセットされる。
これはパーミッション(権限)の指定なのですが、真面目に解説を行うとさらに別関数の解説が必要になりますので、ここでは一旦忘れてください。
ファイル操作でもモードについては多少触れていますので参考にしてください。
詳しく、理解したい人はchmod(2)のmanを参照してください。


ディレクトリ削除

当然残りはディレクトリの削除です。

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    mkdir("aaaa", 0755);

    rmdir("aaaa");

    return 0;
}
やっていることは簡単で、ディレクトリを作成してすぐに削除しているだけです。
rmdirをコメントアウトしたりして実験してください。きちんとディレクトリが削除されているのが分かるはずです。


まとめ

今回作成したプログラムではあえてエラー処理は入れていません。
そろそろ自分でエラー処理を書けるようになりましょう。manを見ながらエラー処理を書く部分は各自の宿題としましょう。

いかがでしたか?
今回紹介した関数は簡単ですが必須の機能です。これらを使用すればmkdir(1)のようなコマンドも作れるはずです。
次回はディレクトリに含まれるファイルの一覧を表示する、いわゆるlsに似た処理に必要な関数を解説していきます。