可 OY Perl Programming モジュールを活用はう zip ファイルと tar ファイル 結城浩 本連載は , モジュールを使って Perl プログラミングを楽しみま す。今回はアーカイバを操作する zip モジュール , tar モジュール を取り上げます。 今回は「アーカイバ操作」 こんにちは , 結城浩です。モジュールを 使って perl プログラミングを楽しむ連載で す。前回の予定ではメールの添付ファイル を扱うつもりでしたが , 添付ファイルにつ いて考えているうちに , アーカイプを行う Archive::Zip モジュールや・ chive::Tar モジ ュールがおもしろくなったので , 予定を変 更します。今回は , Table 1 に示すモジュ ールおよびプラグマを扱います。 Archive::Zip zip ファイルについて インターネットでソフトウェアをダウン ロードするとき , 多くのソフトウェアでは 複数のファイルが 1 つにまとめられて ( アー カイプされて ) います。 W ⅲ dows 系のソフ トは拡張子が . zip になっているファイル ( zip ファイル ) , UN Ⅸ系のソフトは拡張子 が . . gz になっているファイル ( 通称ね rb , ターポール ) が多いようです。日本では拡 張子が . レ h になっている lha のアーカイプも 多く使われています。 zip ファイルの中には任意個のファイル をディレクトリ構造も含めて格納できま す。また格納の際にデータが圧縮されるの で , ファイルサイズを小さくすることがで きます。自分が作成したソフトや文書など の一式をバックアップしたり , ほかの人に メールで送ったり , Web ページで公開した りするときなどにとても便利です。 zip ファイルを取り扱うソフトは , 以下 のリンクから入手することができます。私 は Windows のコマンドプロンプトで Info-Zi p の zip. exe と unzip. exe を使用しています。 ・ Info-Zip http://www.freesoftware.com/ pub/infozip/ ・ WinZip http://www.winzip.com/ TabIe 1 今回扱うモジュールおよびプラグマ Archive::Zip モジュールの入手 そのような便利な zip ファイルを PerI から 直接取り扱うためのモジュールが , Archiv e::Zip です。このモジュールは Perl の標準 モジュールではありません。まだインスト ールしていない場合は , CPAN からダウン ロードする必要があります。たとえば , Fi g. 1 の手順で入手してください。 同様の手順で , Archive::Tar や Compress : : ZIib も入手できます。 A 「 ch ⅳ e : : Zip モジュールのインストール ダウンロードした Archive-Zip-0.11. zip ( フ 名前 Archive::Zip A ℃ hive::Tar File::Find st 「 ict zip ファイルを操作するモジュール ta 「ファイルを操作するモジュール ディレクトリツリーを巡回するモジュー丿レ 厳しい文法チェックを行うプラグマ Fig. 1 A 「 ch ⅳ e : : Zip モジュールの入手 ( 1 ) CPAN のモジュールのページへアクセスする http://www cpan. 0 「 g/modules/index. html ( 2 ) そこから catego Ⅳというリンクを選ぶ ( ミラーリングした別サイトに行くかもしれません ) ( 3 ) カテゴリー覧 ( Fig. 2 参照 ) の中から以下を選ぶ 1 7 A ℃ hiving_and—Comp 「 ession/A 「 chive/ ( 4 ) 最新版のファイルをダウンロードする ※たとえば今回の記事では筆者は , A ℃ hive-Zip-0.11. zip というファイルをダウンロードしました Enjoy perl Programming モジュールを活用しよう 85
きは , . (clcpplhljava)$/i のように指定します。 Archive::Tar Archive::Tar モジュール 今度は tar ファイルを扱う℃ hive::Tar モ ジュールを使ってみましよう。 Archive::Zi p モジュール同様に , new でインスタンス を作り , それを使ってメソッドを呼び出 します。 簡単な例 List 5 に Archive::Tar を使う非常に簡単な 例を示します。これは先ほど Archive::Zip を使ったものと同様のプログラムです。 ta rl. pl というファイルをね r ファイル test. tar に まとめます。 tar ファイルを作成する List 6 (maketar. (l) は , Archive::Tar を使 って引数に指定したファイルやディレク トリを 1 つの tar ファイルにまとめるプログ ラムです。 Fig. 5 List 2 の実行例 ( doc ディレクトリ以下を t 「 ans. zip にまとめている ) Adding doc/pdf/prince. pdf Adding doc/pdf/match. pdf Adding doc/pdf/magi. pdf zip ファイルから readme を抽出する (readme—zipl. pl) List Adding doc/pdf/leaf. pdf Adding doc/smallct. html Adding doc/selfish. html Adding doc/Readme. txt Adding doc/princee. html Adding doc/p 「 ince. html Adding doc/match. html Adding doc/magi. html Adding doc/leaf. html Adding doc/bedtime. html C:YWORK> perl makezipl. pltrans. zip doc Adding doc/ebk/match. ebk Adding doc/ebk/selfish. ebk Adding doc/ebk/prince. ebk Adding doc/ebk/smallct. ebk Adding doc/ebk/magi. ebk Adding doc/ebk/leaf. ebk Adding doc/pdf/selfish. pdf Adding doc/pdf/smallct. pdf use Archive: :Zip; if (@ARGV = 0 ) ( p て土北 "Usage: perl readme—zipl ・ Fig. 6 List 4 の実行例 print $readme; $readme = $ zip—>contents ( $— print ” = = = = if (/readme/i) { foreach ( e は虻 ) { @filelist = $zip->memberNames( $zip = Archive: :Zip->new($zipfile); $zipfile = shift ②駅 G 新 ex ( -1 zipfile%n" ・ C ・ %WORK> perl 「 eadme_zipl 回 t 「 ans. zip このファイルは、 Readme. txt です。 こんにちは、結城浩です。 = . /doc/Readme. txt = ← zip ファイル中の Readme. txt が表示される Enj0Y PerI Programming モジュレを活用はう Archive::Zip では addFile というメソッド 名でしたが , Archive::Tar では add_files と いうメソッド名になっています ( こういう 情報も perldoc Archive::Tar を使って調べま す ) 。 ta 「ファイルから readme を抽出する List 7 は tar ファイルから readme を抽出す るプログラムです。ここではとくに , 拡張 子が . gz になっているもの ( すなわち , tar し たあと gzip プログラムで圧縮したもの ) も 取り扱えるようにしています。もしも拡張 子が . gz になっていたら , read メソッドで 読み込む際に圧縮フラグを付けてやります ( オプショナルな第 2 引数を 1 にします ) 。 こでも , Archive::Zip と Archive::Tar の メソッド名が異なっていることに気づきま す。 Archive::Tar でファイル一覧を得るのは list—files メソッドで , 内容を得るのは get ー content メソッドです。 Archive::Tar モジュ ールの主なメソッドの一覧を Table 3 に示 します。 応用例 バックアップで利用 私は , 原稿ファイルやソースファイルな どをバックアップするときに zip を使って います。仕事が一段落したときに「じゃあ こまでをバックアップしよう」という感 じで zip ファイルを作り , それを別のメデ ィアにコピー ( あるときは CD-RW , あると きはドでサーバに転送 ) します。 単に zip コマンドを使ってアーカイプを 作るだけだと , 機械的にファイルを集める だけで終わります。しかし , Archive::Zip モジュールを使って perl のスクリプトの中 から zip ファイルを操作できると細かな処 Enjoy perl Programming モジュールを活用しよう 89 たり , ファイルサイズが大きすぎる場合に にマッチしたファイルのみをアーカイプし 理を行えます。たとえば , 特定の正規表現
ァイル名はバージョンによって異なる ) は , Fig. 3 のような手順でインストールします。 こでは , 最初の zip ファイルの展開に unz ip. exe を使い , MakefiIe の実行に V1sual C + + の nmake. exe を使っていますが , あなたが 使っている zip ファイルの展開ツールと ma ke を使ってももちろんかまいません。 簡単な例 List 1 に Archive::Zip を使う非常に簡単な 例を示します ( 実行結果は Fig. 4 ) 。これは zipl. pl というファイルを圧縮して , test. zip という zip ファイルを作成するものです。 こで使われている new, addFile , writ eToFileNamed は , Archive::Zip モジュール のメソッドです。 Archive::Zip モジュール にはメソッドがたくさんあるので , 主なメ ソッドの一覧を Table2 に示します。「メン ノヾ」というのは 1 つの zip 中に含まれている ファイルやディレクトリのエントリのこと Fig. 2 CPAN のカテゴリー覧 です。 23_Miscellaneous—Modules/ 22 Mic 「 osoft_Windows ModuIes/ 21_File_Handle 」 nput_Output/ 20_ControI_FIow UtiIities/ 19 MaiI_and_Usenet—News/ 18 」 mages—Pixmaps_Bitmaps/ 1 7 Archiving—and—Comp 「 ession/ 16_Serve 「 _and_Daemon Utilities/ 15_World Wide_Web_HTML_HTTP_CGl/ 14 Secu 「 ity—and—Encryption/ 1 3 」 nte 「 nationaIization_Locale/ 12_Opt_Arg_Param_Proc/ String—Lang—Text_Proc/ 10_File_Names—Systems Locking/ 09—Language 」 nterfaces/ 08_Use 「」 nte 「 faces/ 07 Database lnterfaces/ 06_Data_Type—Utilities/ 05 Netwo 「 king_Devices 」 PC/ 04_Operating—System 」 nterfaces/ 03_DeveIopment—Support/ 02 Perl_Core Modules/ 86 1 1 C MAGAZINE 2001 4 99ーN0t 」 n_ModuIeIist/ 24_Commercial_Softwa 「 e lnterfaces/ Fig. 3 Archive-Zip-0.11. zip のインストー C ・ %WORK> unzip Archive-Zip-O. 1 1 . zip A 「 chive: Archive-Zip-O. 11. zip creating: A 「 chive-Zip-O. 1 1 / creating: A 「 chive-Zip-O. 1 1/t/ inflating: Archive-Zip-O. 1 1/t/testex. t inflating. Archive-Zip-O. 1 1/t/test. t ・中略 inflating: Archive-Zip-O. 1 1 / c 「 C32 ル inflating: Archive-Zip-O. 11/examples/zip 「 ecent. C:%WORK> cd A 「 chive-Zip-0.1 1 C:YWORKYArchive-Zip-0.11 > perl Makefile.pl Checking if you 「 kit is complete. LOOks good W 「 iting MakefiIe fO 「 Archive::Zip C ・ XWORKXArchive-Zip-O. 1 1 > nmake ← zip ファイルの展開 ←ディレクトリの移動 ← MakefiIe.pl を使って MakefiIe の作成 ← nmake を使ってキットの展開 Microsoft 旧 ) Program Maintenance UtiIity Version 1 62.7022 Copyright (C) Microsoft Corp 1988-1997. AII rights reserved. mkdir blib mkdi 「 bliNlib mkdir blibYlib%Archive mkdir blibYarch mkdi 「 blib%a 「 ch%auto mkdir blibYarch%auto*Archive mkdir bIibYarchXauto*A 「 chiveYZip mkdi 「 blib%lib%auto mkdir blib*lib*auto*A 「 chive mkdi 「 bIib%Iib%autoYA 「 chive%Zip cp IibYArchiveYZipYT 「 ee. pm bIibYIibYArchiveYZipYT 「 ee. pm cp lib*ArchiveYZipYMockFileHandIe. pm blib%Iib%Archive%Zip%MockFiIeHandIe. pm cp IibYArchive%Zip%BufferedFiIeHandIe. pm bIibXlibXA 「「 edFiIeHandle. pm cp IibYArchive%Zip. pm bIib%IibXArchiveXZip. pm mkdir blib*script C:YPerlXbin%Pe 「 l.exe - ℃ :%Perl%lib - ℃ :%PerlYlib -MExtUtils::Command -ecp c 「 C32 blib%scriptXcrc32 C:%PerlVbin%Perl. exe - ℃・ YPerl%lib - ℃ :YPerl%lib -e "system qq[p12bat. bat l.shift" blib%scriptYcrc32 C ・ %WORKXArchive-Zip-O. 1 1 > nmake install ←インストー丿レ Mic 「 OSOft ( 日 ) P 「 ogram Maintenance UtiIity Version 1.62.7022 Copyright (C) Microsoft Corp 1988-1997. AII 「 ights reserved. C:XPerl%binXPerI.exe - ℃ uPerlXlib - ℃ -MExtUtils: ℃ ommand -ecp c 「 C32 blib%scripuc 「 C32 - ℃ :%Pe 「 l%lib - ℃・ -e "system qq[p12bat. bat ]. shift" blibYscriptXcrc32 lnstalling *. pm lnstalling *. pm lnstalling C. YPerIYsite%lib%Archive*Zip%MockFileHandIe. pm lnstalling C ・ %Perl%site%lib%ArchiveYZip%Buffe 「 edFileHandle. pm lnstalling C ・ *. bat lnstalling W 「 iting *. packlist Appending installation infO tO C ・ YPerIYlibYperllocal.pod 「 chive-Zip-0.11 > perldoc Archive::Zip ←ドキュメントを読む
は複数のアーカイプファイルに分割したり することもできそうですね。複数のフロッ ピーディスクにアーカイプファイルを分割 する一方で , できるだけフロッピーの枚数 を少なく抑えるようにすることもがんばれ ばできるかもしれません。 定型文書を含める すでにファイルとして存在するものだけ をアーカイプするのではなく , データをス クリプトで自動的に生成することも考えら れます。たとえば , 次のスクリプト (List 8 ) では , 現在日時を含めた readme. ⅸ t を自動 的に生成するアーカイプファイルを作って います。 今月はアーカイバの操作について学びま した。いかがでしたか。次回は , XML 関 次回は「 XM 凵 EnjOY PerI Programming モジュレを活用はう 連のモジュールを扱ってみましよう。 もしもご意見やご質問がありましたら , 本誌綴じ込みの編集部へのハガキでお知ら せくだされば幸いです。また , ご遠慮なく 結城浩 <hyuki@hyuki.com/ にメールをお送 りください。本連載に関するメールには表 題に [ EP ] という文字を含めてくださると助 かります。本連載に関する U 糺は , http://www hyuki.com/ep/ です。 Archive::Tar の例 (tarl. pl) use Archive: :Tar; $tar = Archive: :Tar->new( $tar->add—files( ” ta て 1. p げ $tar—>write( ″ tes セ . a て” List # Archive: :Tar を使う # Archive : : Tar のインスタンスを生成する # ta て 1.6 というファイルを追加する # test 北 a てというセ a てファイルとして書き出す List ta 「ファイルを作成する ( maketa 「 . pl) use st て ict ー use Archive: :Tar; use File::Find; if (@ARGV く = 1 ) ( print STDERR ntJsage: perl maketar. tarfile. tar dir-or-file ex ( 0 my $tarfilename = shift @ARGV; my $ ヒ a て = Archive: :Tar->new( my @files; foreach my $name (@ARGV) { finddepth(*&tar—it, $name); $tar->add—files(@files); $tar->write($tarfilename); ex 辻 ( 0 sub tar-it ( my $name = $FiIe: :Find: return unless ()f $—); p て土 n セ "Adding $name%n" push(@files, $name); :name; ta 「ファイルから「 eadme を抽出する ( 「 eadme 」 a 「 . pl) use Archive: :Tar; if (@ARGV = = 0 ) { print "Usage : perl readme—tar. tarfil List $zip = Archive: :zip->new( $now = localtime( use Archive : : Zip ー 現在日時を含んだ「 eadme. txt をアーカイブ date. zip に入れる List print $ tar->get—content ( $ ー print if (/readme/i) { foreach (@filelist) { ② f e は s セ = $tar->list—files( $tar → read($tarfile); ) 引 se { $tar->read($tarfile, 1 if ($tarfile = ~ /蕚 . gz$/) { $tar = Archive: :Tar->new( $tarfile 咢 shift @ARGV; exit(-l); $zip->writeToFiIeNamed("date. zip"); EOD こんにちは、現在日時は、 $now です。 nreadme. txt" $ z ip->addString ( くく” EOD ” TabIe 3 A ℃ h ⅳ e : : Ta 「モジュールの主なメソッドー メソッド new() add_files(@filenamelist) add—data($filename. $data, $opthashref) 「emove(@filenamelist) 「 ead($tarfilename, $comp 「 essed) w 「 ite($tarfilename, $compressed) 「 eplace—content($file, $content) get—content($file) list_files() extract(@filenames) data() ta 「を作る ファイル一覧を追加する ファイル名 , データの内容 , オプション ( ハッシュリファレンス ) を渡してテータを追加する ( ta 「内の ) ファイルを削除する ta 「ファイルを読み込む ta 「ファイルを書き込む メモリ上の ta 「を戻り値とする 指定したファイルを展開する ファイルの一覧を得る ( ta 「内の ) ファイルの内容を得る ( ta 「内の ) ファイルの内容を置換する 90 C MAGAZINE 2001 4
pe dOC を読む メソッドー覧を得たり , Archive::Zip の 機能をきちんと調べるには , per ー dOC Archive : : Zip というコマンドでドキュメントを読むのが いちばんです。 perldoc は Perl のマニュアルを読むため のプログラムで , Perl をインストールする と自動的に使えるようになります。 Windo 112 02-09-01 13 : 08 zipl ws の ActivePerI には HTML 版のマニュアルも C ・ *WORK> di 「 test. zip C ・ *WORK> perl zipl zipl syntax OK C ・ perl -cw zipl 回 Fig. 4 List 1 の実行例 $zip->addFiIe( nzipl. pl" $zip = Archive: :Zip->new( use Archive : : Zip ー Archive::Zip の例 (zipl pl) List 付いてきますが , コマンドラインでさっと 読めるので , 私は perldoc を愛用していま す。 使います。 List 2 (makezipl. (l) は次のような書式で リプトを作ってみましよう。 をすべて 1 つの zip ファイルに格納するスク 引数で与えたファイルおよびディレクトリ 缶 ch ⅳ e : : Zip の使い方を練習するために zip ファイルを作成する # Archive: : 2 土 p を使う # 翫 chive : : z 土 p のインスタンスを生成する # zipl. というファイルを追加する $zip->writeToFileNamed( est. 2 土 p ” # test. zip という 2 土 p ファイルとして書き出す ←文法チェック ←実行 ←ファイルはできているかな ? 2001 / 02 / 09 13 : 08 194 test. zip C:%WORK> unzip -ltest A 「 chive: test. zip Length Date 1 1 2 1 Name ・一内容ー - 覧 可 OY PerI Programming モジュールを活用はう perl makezip. zipfile. zip filel file2 dirl これで , 引数に指定した filel , file2, dir といったファイルやディレクトリを zipfile. zip という zip ファイルにまとめます。 ディレクトリは再帰的に子ディレクトリま で格納します。 こでは , 再帰的にディレクトリを格納 するのに zip_recurse というサプルーチンを 定義して使っています。 addFiIe を行うときに頭に ". / " を付けてい るのは , ルートディレクトリからアーカイ プした zip ファイルを展開するとき , 再び ルートディレクトリに展開されるのを防ぐ ためです。 . / を付けてカレントディレクト リ以下にしておけば , 展開を行う人の意図 したディレクトリに展開できます。 Archive::Zip モジュールを利用するとき に , List2 では , use Archive : : zip ( : ERROR—CODES); のような書式を用いています。これは :ER ROR_CODES によってエラーコード (AZ_O K など ) もインポートしているのです。 List 2 の実行結果は Fig. 5 です。 ・ FiIe::Find を使う makezipl. pl では自分で opendir を行って ディレクトリをスキャンしました。ところ でディレクトリをスキャンするためのモジ ュールは PerI に用意されています。 File::Fi 解説 TabIe 2 A 「 chive : : Zip モジュールの主なメソッドー覧 メソッド new([$fileName] ) membe 「 s() memberNames() membe 「 sMatching( $regex ) 「 emoveMember( $memberOrName ) writeToFiIeNamed( $fileName ) contents( $memberO 「 Membe 「 Name し $newContents ] ) addDi 「 ectory( $di 「 ectoryName [, $fileName l) addSt 「 ing( $st 「 ingOrStringRef [, $namel ) addFiIe( $fiIeName [ , $newName ] ) ext 「 actMember( $memberO 「 Name [ , $extractedName ] ) replaceMember( $membe 「 O 「 Name, $newMember ) zip を作る。 $fileName があれば, それを最初に読む zip のメンバの配列を得る zip の内部のファイル名一覧を得る 正規表現 $ regex にマッチするメンバの一覧を得る メンバを削除する メンバを置換する メンバを抽出する ファイルを指定した名前で追加する 文字列を指定した名前で追加する ティレクトリを指定した名前で追加する 指定したメンバの内容を得る。 $newContents があれば , 内容を置き換える 指定したファイル名で zip ファイルを作る Enjoy PerI Programming モシュールを活用しよう 8 /
nd です。これを使うと , List 3 のようにな ります。 こでは , File::Find モジュールの findde pth というサプルーチンを利用しています。 このサプルーチンは , finddepth (*&zip—it , $name); という書式で用い , $ name 以下のディレク トリを順番に巡回 ( スキャン ) し , 工ントリ が見つかるごとにサプルーチン & zip ー it を コールバックします。コールノヾックされた サプルーチンの中では , $File::Find::name にディレクトリ名付きのエントリ名が格納 され , $ ーにそのディレクトリ内のエントリ 名が格納されます。 List3 ではいったん配列@ⅱles にエントリ 名を格納してから , addFiIe を呼び出して います。 zip ファイルから readme を抽出する 今度は , zip ファイルからファイルの内 容を抽出してみましよう。ただ展開しても つまらないので , zip ファイルの中に「 REA DME ファイルらしきものが含まれていたら 表示」するようなスクリプトを書いてみま しよう。 zip ファイルの中には , ファイルの内容 を説明した README というテキストファ イルが含まれることが多いです。しかしそ のファイル名にルールがあるわけではない Readme. 1 st README readme. txt ので , 0 List 4(readme-zip1. (l) に示します ( 実行結 でいたらその内容を表示するスクリプトを れ , ともかく readme という文字列を含ん 現」を使いましよう。ファイル名が何であ こういうときには PerI お得意の「正規表 せるにはどうしたらいいでしようか とわかるのですが , それをスクリプトにさ 人間が見れば「あ , これが README だな」 などのようなバリエーションが生じます。 果は Fig. 6 ) 。 こでは , elist にファイル名を得て , そのあとの forea ch 文でその中から , /readme/i いったん配列@fil List if (@ARGV く = 1 ) { use Archive: :zip , ( :ERROR—CODES); use strict; zip ファイルを作成する (makezipl. pl) ています。 / i というのは大文字小文字を無 という正規表現にマッチするものを見つけ 視する指定です。 もし , 拡張子が . ⅸ t のもののみを表示し たかったら , . txt$/i のように指定します。もしも , 拡張子が . c , ・ cpp, . h, . java のもののみを表示したいと print STDERR "Usage: perl makezipl. pl zipfile. zip d 土て -0 て - f e ex 辻 ( 0 my $zipfilename = shift @ARGV; my $zip = Archive: :Zip->new( foreach my $name (@ARGV) { &zip—recurse($zip, $name); my $result = $zip->writeToFiIeNamed($zipfiIename); if ()d $name) { my ($zip, $name) = 釭 sub zip—recurse { ex 辻 ( 0 die ”引” if ($result ! = AZ-OK) { opendir(DIR, $name); foreach (readdir(DIR) ) next if ( / 、 . / &zip—recurse( $ziP' ) elsif ()f $name) { print "Adding $name%nn $zip->addFile($name, } else { . /$name" # ファイルを追加 print STDERR ” ? $name%n"; push(@files, $name); print "Adding $name%n" return unless ()f $-); my $name = $FiIe: :Find: :name; sub zip—it { exit(0); die ″引” if ($result ! = AZ-OK) { my $result = $zip->writeToFileNamed( $zipfilename); $zip->addFile($name, ″ . /$name" foreach my $name (@files) { finddepth ( *&zip—it , $name foreach my $name (@ARGV) { my @files; my $zip = Archive: :Zip->new( my $zipfilename = shift @ARGV; ex ( 0 p て土 n セ STDERR "Usage: perl makezip2. pl zipfile. zip d 土て一 0 て一 f e if (@ARGV く = 1 ) { use File: :Find; use Archive: :Zip Ⅱ :ERROR—CODES); use strict; File::Find を使ってディレクトリをスキャン (makezip2. pl) List 88 C MAGAZINE 2 1 4