1. 程式人生 > >探索Perl的File::Find模組

探索Perl的File::Find模組

如果你具有任何UNIX方面的經驗,你自然就會發現你需要用到find命令,這個命令對在整個檔案系統中搜索檔名十分有用,特別是你可以使用外卡來匹配檔名並迴圈地穿過任何的目錄結構(許可情況下)。UNIX的find命令可以對找到的檔案執行其他的命令。

Perl之中的File::Find模組包含有相同的功能並給你帶來程式設計性結構方面的優勢。為了展示它的工作方式,我將給出一個使用File::Find模組的指令碼例項。

簡易例項

這個Perl指令碼可以幫助你清理你的PC硬碟,這是通過尋找以.tmp, .chk或是.zip結尾的檔案或是以~符號開始的檔案來完成的(你可以在表A中看到整個指令碼)。指令碼將輸出它找到的每一個檔案的完整路徑,而且在最後會顯示出所佔用的位元組數。如果已經安裝了Perl,你就可以在Windows或UNIX上執行這個指令碼。注意在UNIX環境下,你必須修改指令碼的第一行:改變/bin/perl

路徑來匹配環境中的Perl路徑。在這個例子中,我假設你是在Windows環境下執行這個指令碼。

解決方案

當然,微軟公司的GUI Find也提供了這些功能中的一部分,但我還是編寫了指令碼。這是由於一旦你在Perl中有了這個檔案,你就可以用它來做各種事,例如尋找一個特定的模式,自動地將其刪除,或將其作為另一個應用程式的輸出。

我在標準Perl庫和Perl函式中設定了一個模組,因此當你的機器上安裝了Perl之後,所有必要的模組就都可以使用了(我從ActiveState獲得了Perl)。File::Find函式模仿了UNIX的find命令並將穿過一個檔案樹。這裡是此方式的API:

Find(&yoursubroutine, ‘dir1’, ‘dir2’…);



你提供的子程式,我將在後面詳細敘述。還有你希望進行搜尋的目錄的列表,記住這些目錄將以一種深度優先方式被穿過。

我使用的另一個方式就是stat()函式(與C的同名庫函式類似)。它給出各種關於將檔案作為一個爭議對待的資訊,表B顯示了此方式的API。

這裡要注意函式返回的值位於一個列表之中,唯一一個我們感興趣的值就是$size,它包含了給定檔案的大小(用位元組表示)。

所有工作都會在子程式中被執行,要記住每一次遇到一個檔案時,它都會被呼叫,所以,我們的工作就是要判定檔名是否與我們要尋找的檔案相匹配。

File::Find方式具有特殊變數,將被賦予特定的資訊,顯示如下:


  • $_包含目錄中的當前檔名

  • $File::Find::dir包含當前目錄名

  • $File::Find::name包含$File::Find::dir/$_

當子程式被呼叫時,你就會確實位於變數$File::Find::dir的目錄中。你可以在表C中看到,子程式使用常規表示式來與$_匹配,$_使用一個if宣告來尋找我們前面詳細給出的所有檔名。

如果在$_之中儲存的檔名與if聲 明中的五個常規表示式中的任何一個相匹配,我們就將在其下面輸入程式碼塊,常規表示式非常的簡單,“.”代表一個文字上的點號而不是常規表示式中的“.”的 特殊意義。我們使用“”符號來避開特殊意義。“$”代表一個字串最後的匹配而“^”代表與開頭匹配。表A顯示了我們試圖將其與相對應的常規表示式相匹配 的檔案。

File that ends with .zip

/.zip$/

File that ends with .tmp

/.tmp$/

File that ends with .TMP

/.TMP$/

File that begins with ~

/^~/

File that ends with .chk

/.chk/

這裡要注意的是指令碼對小寫的tmp和大寫的TMP同時進行查詢,而出於效率方面的考慮,你可以將檔名改為大寫並只查詢TMP匹配。

最後,指令碼使用stat()函式來記錄所有與if宣告之中的某個條件相匹配的檔案所使用的位元組數。如果條件符合,指令碼將儲存$size之中的值並將其加入到$ByteCount記錄變數,如下面的程式碼所示:

$ByteCount += $size;

要在你的機器上執行指令碼,在DOS命令提示視窗下鍵入下面的命令:

Perl diskrpt.pl

這裡假設了你已經修改了你的PATH變數來包括可執行程式,且你已經將utility儲存在名為Diskrpt.pl的檔案之中。命令的輸出將在DOS命令提示視窗中出現。

在後面的文章中,我將介紹到指令碼的修改,例如刪除某個檔案型別或將結果交於另一個應用程式。

perl下的File::Find模組具有shell下的find命令的功能,下面具體看2個例子:

1,找出某個目錄下面以*.old結尾的檔案

#!/usr/bin/perl -w

use strict;
use File::Find;

my $path = '/home/test/';
sub wanted {
    if ( -f $File::Find::name ) {
        if ( $File::Find::name =~ /\.old$/ ) {
            print "$File::Find::name\n";

        }
    }

}

find( \&wanted, $path );


2,找出某個目錄下面幾天前的檔案

my $path = '/home/test/';
opendir DH, $path or die "cannot chdir to $path : $!";

for my $file (readdir DH) {
    next if $file eq "." or $file eq "..";
    next if $file =~ /^\./;
    if (time() - (stat($path.$file))[8] > (60*60*24*7)) {
        print $path.$file."\n";
    }
}
closedir DH;