ディレクトリの作成と削除2
■この章で解説するモジュール
File::Path (mkpath, rmtree)
■この章でできること
・ディレクトリを再帰的に作成する
・ディレクトリを再帰的に削除する

 ディレクトリの作成と削除1では組み込み関数のmkdirやrmdirを使って単一のディレクトリの作成や削除をする方法を解説しましたが、File::Pathモジュールを使うと、複数のディレクトリを再帰的に一括作成したり、ディレクトリの中のファイルやサブディレクトリをごっそりと一括削除したりできます。

 では、まずはディレクトリ作成の簡単なサンプルです。

#!/usr/bin/perl

#############################################
# ディレクトリ作成のサンプル1
# Author: "Perl Programming Tips"
#############################################

use File::Path;

@pathArray = ('dir1/subdir1/subdir2', 'dir2/subdir1');

# ディレクトリの作成
mkpath(@pathArray, {verbose => 1});

 実行結果は次のようになります。

[g@710m temp]$ tree
.
|-- file12_01.pl
`-- file12_02.pl

0 directories, 2 files
[g@710m temp]$ perl file12_01.pl 
mkdir dir1
mkdir dir1/subdir1
mkdir dir1/subdir1/subdir2
mkdir dir2
mkdir dir2/subdir1
[g@710m temp]$ tree
.
|-- dir1
|   `-- subdir1
|       `-- subdir2
|-- dir2
|   `-- subdir1
|-- file12_01.pl
`-- file12_02.pl

5 directories, 2 files
[g@710m temp]$ 

 ディレクトリパスを作成するには、File::Path::mkpath関数を使います。この関数は指定したディレクトリパスをサブディレクトリまで含めて作成します。UNIXの「mkdir -p」のような動作をします。ディレクトリパスは絶対パスまたは相対パスで指定します。

 mkpathの第一引数にはディレクトリパスをリストで指定できるので、複数のディレクトリパスを一度に作成できます。第二引数にはmkpathの動作に関するオプションをハッシュへのリファレンスで渡します。省略した場合はデフォルトの動作になります。

File::Path::mkpath
指定したディレクトリパスを再帰的に作成する。
書式
mkpath @path, [\%options]
引数
path:
作成するディレクトリパスのリスト
options:
オプションを指定したハッシュへのリファレンス
戻り値
実際に作成したディレクトリのリスト

 mkpath関数に指定できるオプションには次のようなものがあります。

オプション
意味
verbose
指定すると、ディレクトリを作成するたびにディレクトリ名を出力する。デフォルトでは何も出力しない。
mode
作成するディレクトリのパーミッションを指定する。デフォルトは0777。この値にumask値で修飾された値が実際のディレクトリのパーミッションになる。すでにディレクトリが存在する場合は、パーミッションは変更されない。
error
リファレンスを渡すと、リストへのリファレンスにして返す。エラーが発生した場合に、ディレクトリ名とエラーメッセージをハッシュに格納し、そのハッシュへのリファレンスをリストに格納する。エラーが発生しなかった場合でも空リストへのリファレンスを返す。

 次は、ディレクトリツリー削除の簡単なサンプルです。

#!/usr/bin/perl

#############################################
# ディレクトリ削除のサンプル1
# Author: "Perl Programming Tips"
#############################################

use File::Path;

@pathArray = ('dir1', 'dir2');

# ディレクトリの削除
rmtree(@pathArray, {verbose => 1});

 実行結果は次のようになります。

[g@710m temp]$ tree
.
|-- dir1
|   `-- subdir1
|       `-- subdir2
|-- dir2
|   `-- subdir1
|-- file12_01.pl
`-- file12_02.pl

5 directories, 2 files
[g@710m temp]$ perl file12_02.pl 
rmdir subdir2
rmdir subdir1
rmdir dir1
rmdir subdir1
rmdir dir2
[g@710m temp]$ tree
.
|-- file12_01.pl
`-- file12_02.pl

0 directories, 2 files
[g@710m temp]$ 

 ディレクトリツリーを削除するには、File::Path::rmtree関数を使います。この関数は指定したディレクトリ以下のファイルやサブディレクトリを一括で削除します。UNIXの「rm -rf」のような動作をします。

 rmtreeの第一引数にはディレクトリをリストで指定できるので、複数のディレクトリツリーを一度に削除できます。第二引数にはrmtreeの動作に関するオプションをハッシュへのリファレンスで渡します。省略した場合はデフォルトの動作になります。

File::Path::rmtree
指定したディレクトリ内のファイルやディレクトリを再帰的に全て削除する。
書式
rmtree @directory, [\%options]
引数
directory:
削除するディレクトリツリーのリスト
options:
オプションを指定したハッシュへのリファレンス
戻り値
削除したファイル数

 rmtree関数に指定できるオプションには次のようなものがあります。

オプション
意味
verbose
指定すると、ファイルやディレクトリを削除するたびに名前を出力する。デフォルトでは何も出力しない。
safe
指定すると、削除する権限のないファイルやディレクトリはスキップする。言い換えると、ファイルのパーミッションの変更を試みない。
keep_root
指定すると、指定したディレクトリの中身は削除するが、指定したディレクトリ自体は削除しない。
result
リファレンスを渡すと、リストへのリファレンスにして返す。ファイルやディレクトリを削除するたびに名前をリストへ格納する。削除しなかった場合でも空リストへのリファレンスを返す。verboseの代わりに使うと便利。
error
リファレンスを渡すと、リストへのリファレンスにして返す。エラーが発生した場合に、ディレクトリ名とエラーメッセージをハッシュに格納し、そのハッシュへのリファレンスをリストに格納する。エラーが発生しなかった場合でも空リストへのリファレンスを返す。

 次に、mkpath、rmtree関数のエラー処理について補足しておきます。デフォルトではmkpath、rmtreeともに、エラーが発生した場合はCarp::carpを通してSTDERRにメッセージを出力します。致命的エラーが発生した場合はCarp::croakを通してプログラムを終了します。

 この動作を変更したい場合は、errorオプションを使います。以下にerrorオプションを使ったエラーハンドリングのサンプルを示します。

#!/usr/bin/perl

#############################################
# ディレクトリ削除のサンプル2
# Author: "Perl Programming Tips"
#############################################

use File::Path;

@pathArray = ('dir1', 'dir2');

# ディレクトリの削除
$ret = rmtree(@pathArray, {error => \$ref_err});

foreach (@$ref_err)
{
    ($file, $message) = each(%$_);
    print "file:" . $file . " message:" . $message . "\n";
}

 実行結果は次のようになります。

[g@710m temp]$ ll
合計 12
drwxrwxr-x 2 g g 4096 2008-07-29 05:58 dir1
drwxrwxr-x 2 g g 4096 2008-07-29 05:58 dir2
-rw-r--r-- 1 g g  413 2008-07-29 05:56 file12_03.pl
[g@710m temp]$ ll -d ../temp/
dr-xr-xr-x 4 g g 4096 2008-07-29 05:59 ../temp/
[g@710m temp]$ perl file12_03.pl 
file:dir1 message:cannot remove directory: 許可がありません
file:dir2 message:cannot remove directory: 許可がありません
[g@710m temp]$ 

 この例では、あらかじめディレクトリ「dir1」「dir2」を作成した後、親ディレクトリの書き込み権限をとってあります。こうするとディレクトリが削除できなくなります。この状態でrmtree関数を実行してみたのが上の実行結果です。

 rmtree関数のオプションで「error」にリファレンス変数を渡します。このリファレンスはrmtreeの内部でリストへのリファレンスになります。このリストには、エラーが発生したファイル名をキー、その時のエラーメッセージを値としたハッシュへのリファレンスが格納されます。これをたどっていくと、エラーが発生したファイルとメッセージの一覧を取得することができます。