Perl スクリプトでワイルドカードを考慮する
MINI SERIES
はじめに
Perl にて引数で指定したファイルを操作するときに、ワイルドカードも考慮してプログラムを組みたいなと思ったので調べてみました。今回はとりあえず Cygwin に付属していた Perl 5.8.1 にて実験を行い、その後で Linux 上での動作実験を行っています。
ワイルドカード
そもそもワイルドカードと言うのは、コマンドラインから実行する際に、ファイル名を一括して指定することが出来るようにするためのものです。
たとえば "file*.txt" というように "*" という任意の文字に該当するワイルドカードを使用すれば、もちろん存在しているファイルに限りますけど、"file1.txt" や "file2.txt" それに "file100.txt" といったファイルが該当します。
もうひとつ "?" という任意の一文字に該当するワイルドカードもあって、たとえばこれを "file?.txt" として使用すれば、"file1.txt" や "file2.txt" といったファイルが該当します。あくまでも一文字ですので "file100.txt" は該当しません。
FILEGLOB を使用する
ワイルドカードから該当するファイル名を取得するために、Perl では FILEGLOB (ファイルグロブ) という機能が用意されていました。
これは < > という記号でワイルドカードを含むファイル名をくくることで、それに該当するファイルの一覧を配列変数 ( @ ) として取得できるというものです。これを用いることでいとも簡単に、ファイルリストを取得することが出来ました。
直接記述する場合
たとえば、次のように使用します。
@list = <file*.txt>;
たったこれだけで、該当するファイル名を @list 変数に取得することが出来ます。なお、< > の中に含める文字列は引用符でくくる必要はありません。逆にくくってしまうと、リストが取得できずに終わってしまうようなので気をつけましょう。
実際には、さらにパス情報も含めて使用することが多いと思います。
@list = </usr/local/etc/file*.txt>;
このようにファイルリストを取得した場合には、パス情報もしっかりとリストの中に含まれます。上記では絶対パスですけど、相対パスの場合もそのまま相対表記で現れてくるようでした。逆にパスを含めない場合は、パス情報なしで反映されてくるようです。
変数を用いる場合
FILEGLOB 内では変数も使用することが出来ます。
変数を用いることでプログラムをより状況にあったものにすることが出来るのですけど、この場合には気をつけなくてはいけないことがいくつかありました。
まずは、変数を含めた場合の使い方を挙げてみます。
my $file = "file*.txt";
my $path = "/usr/local/etc";
@list = <$path/$file>;
このような感じにしようすることで、"/usr/local/etc/file*.txt" に該当するファイル名を取得することが出来ます。不思議な感じがしますけど、$path/$file を引用符でくくってしまうとリストが取得できないようなので注意です。
また、次のように変数だけを FILEGLOB 内で指定することは出来ないようです。
my $file = "/usr/local/etc/file*.txt";
@list = <$file>;
このように < > の中に変数だけを記述してしまうと、リストの取得は出来ませんでした。
glob() 関数を使用する
FILEGLOB と同等の機能を持つ関数として glob() 関数があります。
先ほどに FILEGLOB を用いて変数を使用する場合の注意点を挙げましたけど、変数を使用する場合は、もしかするとこの glob() 関数を用いた方が簡単かもしれないです。
glob() は関数ですので、引数は文字列型で渡します。
# 直接指定する場合
@list = glob("/usr/local/etc/file*.txt");
# 変数を二つ組み合わせる場合
@list = glob("$path/$file");
# 変数のみで指定する場合
@list = glob($file);
Perl はダブルクォーテーションでくくった文字列内で変数を展開することが出来るので、変数の値に応じて取得したいファイルリストが変化するような場合には、glob() 関数を使用した方が直感的ですし、正常動作も期待しやすくて良さそうな感じがしました。
FILEGLOB や glob() 関数が返す値
FILEGLOB や glob() 関数にてワイルドカードを解決してもらった場合、結果は次のような感じになります。
まず、渡された文字列にワイルドカードが使用されていた場合、該当するファイルが存在していた場合には、それらが配列として一度に全部返されます。もしも該当するファイルが存在しなかった場合には、値の何も保存されていない配列が返されます。
そして、渡された文字列にワイルドカードが含まれて居ない場合には、そのファイルがあるかどうかに関わらず、配列にその指定された文字列が格納されて返されるようです。
変数を用いる場合の注意?
FILEGLOB または glob() 関数を用いて変数からワイルドカードを解決する場合、注意すべきことがあるようです。
それは、この機能の性質上、任意のプログラムを実行される可能性があるということでした。ワイルドカードの解決には、シェルと呼ばれるコマンドラインで命令を実行するための機構が利用されるため、続けて違う命令を連結されてしまうと、それを実行してしまうのだそうです。
具体的には、セミコロン ";" で区切って、続けて実行したい命令を置けばいいようなのですけど…。
実際に試してみましたけど、特に動作する気配はありませんでした。自分の試し方が間違えているのか、それとも Perl 側で何かしらの対応が取られたのかはわかりませんけど、とりあえず、ファイル名に ";" が含まれていないかを調べるなど、念のため注意してプログラムを組むのが良さそうです。
引数からファイル名を取得する場合
Perl では、引数に渡した値が @ARGV 配列に格納されます。これを元にワイルドカードの補完を行う場合にも、少し気をつけておいたほうが良さそうなことがありました。
引用符を使用しないでワイルドカードを含めたファイル名を指定した場合、@ARGV に格納された時点で、既にワイルドカードの解決が行われてしまうようです。たとえ引数が 1 つであっても、それにワイルドカードが含まれていて、かつ、該当するファイルが存在すると、その数だけ引数が指定されたこととなるようでした。
該当がない場合には、ワイルドカードが置き換えられないまま、引数として渡されます。
引数から、ワイルドカードを保ったままプログラムへ値を渡したい場合には、引数を引用符でくくってあげる必要があるようです。そうすることでそのまま文字列として渡されますので、ワイルドカードの処理をプログラム内で行うことが可能となります。