Perl小技巧:文件操作

  • 翻 譯:SaladJonk
  • 審 校:qiang
  • 出 處:中國Perl協會 FPC(Foundation of Perlchina)
  • 原 名:Perl circus:file operation
  • 作 者:Luke Melia
  • 原 文:
  • 發 表:2002

Perlchina提醒您:請保護作者的著作權,維護作者勞動的結晶。

目錄

 

Find : 找到具有指定特徵的文件

$path = "/path/to/dir/";
opendir DIR, $path;
@arr1 = readdir DIR;
@arr2 = grep{-T "$path$_"} @arr1; #text files only
@arr3 = grep{!-d "$path$_"} @arr1; #no directories
@arr4 = grep{-s "$path$_" < 1024} @arr1; #less than 1K

代碼解釋:假如被測試的目錄項是一個文本文件,那麼 -T 文件操作符就會返回真。其實針對目錄項的測試操作還有很多。(注:文件和目錄在系統中都是以目錄項的形式來管理的,所以要區別一個目錄項指向的是一個文件 還是一個目錄需要相應的操作符)。注意上面的 readdir 函數返回指定目錄下的所有目錄項。因爲在 grep 函數中對目錄項的測試需要文件的完全路徑,所以我們把 $PATH(存儲了目錄項的部分路徑) 和 $_(存儲了目錄項的名字)中的內容聯合起來得到文件的完全路徑

搜:對目錄進行遞歸搜索

use File::Find;

find(/&handleFind, 'imac:documents:code');

sub handleFind{
my $foundFile = $File::Find::name;
print "$foundFile/n" if ($foundFile =~ //.html?$/i);
}

運行結果: imac:documents:code:index.html imac:documents:code:perl:example.HTM

代碼討論:那些工作於 Unix 系統的 Perl 程序員可以非常簡便的利用 UNIX 上提供的工具來完成許多日常的工作,比如遞歸的列出指定目錄下的所有目錄項(也就是列出指定目錄及指定目錄子目錄下的所有目錄項目)。然而 Perl 的一個最大的特徵就是可以運行於很多的平臺上。所以如果你碰巧工作在一個非 UNIX 的平臺,或者如果你雖工作在 UNIX 平臺,但不喜歡使用系統工具寫腳本,你可以選擇 Perl。要完成這些巧妙的工作,你需要使用 perl 中的 File:Find 模塊。當你加載了這個模塊的時候,你就可以使用其中的 find 子函數,在調用這個函數的時候,需要帶參數:第一個參數是一個函數的引用,這個函數由你自己建立,每次一個文件被找到的時候,它都會運行。接下來的一個參 數是一串你想要搜索的路徑。我寫的這個示例腳本是運行在 Macintosh OS 8.x 系統上的,所以我使用了 Mac 系統的路徑分隔符 :。如果是在 Windows,你可以用反斜槓,如果是在 Unix 系統則是正斜槓(至於在 Amiga 系統上用什麼我就不知道了)。總之,find 函數將會在每次找到一個文件的時候調用你給出的子函數,而且會對子目錄進行查找。在我的 handledfind 子函數中,我通過這個模塊特定變量 $File::Find::name 來獲得每次 find 找到的文件名。然後,就可以對該文件執行任何你想的測試,在上面的例子中,我們輸出有 .html 的擴展名文件名。

讀 :一次讀入整個文件內容

open FH, "< anthem";
$/ = undef;
$slurp = <FH>;
print $slurp;

運行結果:一下就顯示了所有的文件內容,此刻你應該非常的自豪。:) 代碼討論:尖括號 <> 對文件句柄進行操作,在標量上下文中它將返回文件的下一條記錄,在數組上下文中它將返回所有的記錄。在默認的情況下,文件中的記錄被認爲是由換行符分開 (例如回車或其他代表新行開始的字符)。你可以重新設定這個默認的分隔符,然後 Perl 將會以你指定的分隔符爲準來替代換行符。全局變量 $/ 裏存儲了輸入文件的分隔符,如果你把 $/ 的值設置爲 undef ,那麼 Perl 將會認爲整個文件是一條記錄(因爲此刻已經沒有文件分隔符了)。牢記 $/ 是全局變量,千萬不要在腳本的其他地方不經意的改變它,這個錯誤將很難被發現。你可能會問,我們能否不改變 $/,而採用把文件的所有記錄讀到一個數組中,然後把數組聯合成一個很長的字符串(比如 $slurp = join("",<FH>);)的方法實現一次讀入文件。當然這也是一個有效的解決辦法,但是你會發現它很慢,是否選用它取決你的應用,取決 於你是否關心運行速度。

賦值 : 把一個文件句柄賦給另一個文件句柄

open(MYOUT, "> bottle.txt");
*STDOUT = *MYOUT;
print "message";

運行結果:文本文件 bottle.txt 現在包含 message 字符串。代碼討論:以前可能你配合使用過 Print 函數和文件句柄,但是你是否知道就算你沒有使用文件句柄,Perl 也默認你在使用一個稱爲 STDOUT 的句柄?C 程序員知道 STDOUT 代表標準輸出,也就是通常的屏幕,或終端窗口(或者是 CGI 程序的輸出端 - 瀏覽器)。在這裏我們完成的工作是創建我們自己的文件句柄,它指向一個給定的文件,然後我們做了一件比較鬼的工作,使用 * 前綴把 STDOUT 轉換爲 typeglob 類型。Typeglob 類型的數據可以有別名,這樣一個變量可能會指向另一個其他名字的變量。上面第二行代碼使 STDOUT 指向 MYOUT 變量。所以執行 print 操作時的默認輸出對象也就成爲了我們創建的文件句柄。

寫:同時向兩個文件句柄執行寫操作

use IO::Tee; 
$tee = IO::Tee->new(">> debuglog.txt", /*STDOUT);
print $tee "an error ocurred on ".scalar(localtime)."/n";

運行結果:an error ocurred on Fri Feb 23 21:44:20 2001 代碼討論:如果,由於種種原因你想要同時向兩個位置寫入同一個字符串,這和 UNIX 下的 tee 工具的用途一樣。即使你不是工作在 Unix 平臺上,Perl 也通過 Tee 模塊爲你提供這個功能。Tee 模塊可以在 CPAN 下載,你應該把它安裝到 Perl 的 IO 庫文件夾中。Tee 模塊以 OOP 方式編寫,所以使用它之前你應該首先使用它的 new 方法來創建一個 Tee 對象,整個過程需要兩個參數,每個參數既可以是代表文件句柄的字符串,也可以是一個對已打開的文件句柄的引用。在上面的例子中,我們用一個字符串來代表一 個以附加模式打開的文件句柄,它指向名爲 debuglog.txt 的文件,另一個參數是系統內置的文件句柄 STDOUT,整個句柄是系統自動創建的,print 函數默認情況對它進行操作。爲了得到一個文件句柄的引用我們需要對一個 typeglob 類型的數據使用反斜槓。Typeglob 可以代表任何已命名的某個變量,不論它是數組,散列還是標量等。使用 * 很有必要,因爲文件句柄自己沒有前綴符號。new 操作符返回 Tee 類的一個實例對象,然後我們把整個實例賦給 $tee 標量。現在,無論什麼時候我們向 $tee 進行寫入操作,我們都同時向兩個位置進行寫操作。

More: 從一個文件的完全路徑中找出它的名字

use File::Basename;
$path = "/docs/sitecircus.com/html/tricks/trick.of.the.week.html";
$basename = basename($path, ".html");
print $basename;

運行結果:trick.of.the.week 代碼討論:好了,成功了。問題是要找出文件的名字,要不帶任何路徑前綴,不帶任何擴展名。File::Basename 模塊可以使這很容易實現,我們只需要把文件的完全路徑還有要剔除的擴展名傳給它。上面的 path 變量是文件的完全路徑,注意文件分隔符是 /,這個字符很特殊,因爲它是操作系統的保留字符。這裏你不能在文件名裏使用系統的分隔符。你應該知道當今流行的操作系統都使用自己獨特的文件分隔 符:Unix使用 /,Windows 使用 /,Macintosh 使用 :(順便說一下,在 Windows 上的 Perl 腳本中,你既可以使用 /也可以使用 /作爲文件分隔符,Perl 的解釋器能理解你的意思)。File::Basename,當然,能正確在完全路徑中找到文件名,不論時在什麼系統下。

改:改變文件的所有者

($uid, $gid) = (getpwnam($username))[2,3]
or die "$user not in passwd file";
chown ($uid, $gid, $file)
or warn "couldn't chown $file.";

運行結果:無輸出代碼討論:有的時候,你可能知道一個用戶名,而你想用這個用戶名做些事,比如改變一個文件的所有者。但是不幸的是,Perl 的 chown 命令不能接受用戶名作爲參數,但是可以接受一對數字:userid 和 groupid。雖然有這些不便之處,Perl 並沒有讓我們陷入困境,我們可以把用戶名作爲 getpwnam 函數的參數,獲得一個數組,裏面包含了用戶名對應的 userid 和 groupid,分別對應着數組裏的第二和第三個元素。

作者:Luke Melia

發佈了48 篇原創文章 · 獲贊 2 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章