最近接到一個任務是這樣的,一台Solaris服務器上需要運行一個腳本,每天統計MySQL數據庫中的數據並生成報表。本來這是一個可以就事論事的小項目,但是為了以後的靈活和可擴展性,我設計了一個使用XML做統計模版配置的方案。由於在Bash下不太好實現XML的訪問,因此我考慮用Perl來實現這個腳本。
Perl是一個強大的腳本語言,本來是設計應用在文本處理方面的,但是後來發展的越來越強大,已經可以處理網絡、圖形、系統、文件等等各個方面的內容。Perl本身內置了豐富的操作符和函數,外部也有多年積累下來的大量模塊。但是不知道什麼原因在國內好像很少有人用。關於Perl的歷史我就不多說了,有興趣可以上網查一下。有一點要說的是,目前Perl最新的版本是5.8.6,而Perl6雖然已經設計很久了但是由於自舉問題目前還沒有一個可用的版本。為了解決這個問題,台灣的唐宗漢發起的Pugs項目正在快速的實施中,可能很快就能有結果了,有興趣的朋友可以多多關注一下,也許還可以為開源世界做點貢獻。
雖然很早以前就了解過Perl,但是從來就沒有實際的用它做過項目,因此這次的實現是一個邊學邊做的過程。作為一個程序員,學習一種新的語言總會有一點慣性思維,加之Perl在語法上與C語言比較類似。因此我想在這片文章中主要以C為背景做一個比較。這種比較不是比較語言上的優劣,而是說明同樣的功能如何在Perl中實現以及之間的區別。限於篇幅,具體的技術實現的細節我就不在這裡多說了,你可以在末尾的資源一節中找到很多相關的文章。如果你沒有接觸過Perl,我想你可能更希望看到學習Perl的過程中可能會遇到的一些問題以及解決方法。
工欲善其事,必先利其器
要寫代碼,首先至少得有一個編輯器。Perl是跨平台的一種解釋型語言,可以在Unix/Linux/Windows/Mac等平台上運行。具體對應平台上的編輯器,最簡單的方案是Unix下用vi,Windows下用UltraEdit。當然也有商業化的IDE,不過我嘗試了一下發現並不是那麼的好用,因此我在Windows平台上以UltraEdit作為編輯環境,完成後移植到Solaris平台上。
關於環境的搭建,有這樣幾個需要注意的地方:
1、 Windows平台下對應的是ActivePerl,可以免費下載。
2、 去UE的網站上下載Perl的AutoComp文件,可以實現自動完成功能。
3、 下載Perl對應的語法加亮的Tag文件並加入到UE中,可以更塊的發現拼寫錯誤。
4、 在UE設置一個快捷工具,命令行為C:\Perl\bin\perl.exe "%F"(捕獲輸出),可以實現快速運行並顯示結果。
5、 如果你不喜歡UE,那麼我推薦Source Insight
巧婦難為無米之炊
起始從某種角度來說,程序員和廚子是一樣的。要做出一桌大餐來,首先得看看手上有什麼原料,然後才能琢磨一下用這些東西能做出什麼好吃的來。或者說想做什麼東西,得先備好料才行。
看看我們現在都有什麼:一個編輯器,一個Perl的開發環境,還有一個聰明的腦袋。這個任務中,我要處理命令行參數、訪問MySQL數據庫(SQL)、讀寫XML的配置文件以及輸出一個固定格式的報表文件。
好了,去查查資料,看看訪問數據庫和讀寫XML都需要什麼東西。正如同C語言本身帶了很多標准函數庫一樣,Perl本身也有函數庫,並把這些函數庫稱為Module(模塊)。查了一下資料,發現要訪問MySQL數據庫需要DBI和DBD::MySQL兩個模塊,那麼去哪裡找這些模塊呢。這裡給大家介紹一個Perl的Module集散地 ,這裡包含了八千多個Module,可以從這裡下載到幾乎各種各樣的Module。可以手工下載後安裝,也可以使用工具來自動安裝。在Windows下是可以使用ppm進行自動安裝,例如DBD的安裝過程如下:
C:\>ppm
…
ppm> search DBI
Searching in Active RepositorIEs
… 一大堆與DBI相關的包的列表,其中就包括DBI這個包
ppm>install DBI
…
ppm>install DBD::MySQL 如果知道模塊的名字也可以直接安裝
…
ppm>quit
如此就安裝完成了。附帶說一下,Linux下沒有ppm,但是有類似的方式。輸入命令行
perl –MCPAN –e shell
然後install DBI; install DBD-MySQL,和上面的操作幾乎是一樣的。
提示:如果是在Linux下安裝DBD::Mysql模塊,需要把mysql的bin目錄包含在環境變量PATH中,否則會提示找不到mysql_config文件。mysql一般是安裝在/usr/local/mysql下,因此可以通過執行命令行PATH=$PATH:/usr/local/bin/MySQL/bin來將此路徑加入到環境變量中。
訪問XML有幾種包可以選擇:使用DOM和Simple模塊。Simple模塊是把XML用Perl的數組方式表示,而DOM是W3C維護的一個基於樹的XML文檔標准。具體用哪種就看個人的需要了。我使用的是DOM,因此要安裝XML-DOM包,方法同上。
芝麻開門
說起編程語言,簡單的來說無非就是這樣幾個必不可少的基本元素:變量、數據、表達式、流程控制語句(包括條件、分支、循環)、函數、對象。具體到語言上,大部分的內容只是表達的形式不同而已。而Perl與C又有什麼區別呢?
首先要知道,Perl是一種腳本語言。所謂的腳本,就是沒有主函數,從最開始一行一行的按照順序解釋執行(老版Basic不也是如此嗎)。因此,盡管把你的思路轉化為流程用Perl表達出來吧。
其次,Perl的設計中參考了很多語言的長處,並避免了設計上的缺陷。因此Perl的很多語法你可能都會覺得似曾相識。我把Perl的語法總結了一下,和C語言做了一個簡單的對比表格。表格左右兩邊的語句是C和Perl對應表達同一個功能各自的不同方式。如果讀者有C語言的經驗,相信看到這個對比可以很快的上手吧?
語法元素
C Perl Perl語法說明
注釋
/* … */
# …
只支持單行注釋
變量
int a, b, c;
char c=’A’;
int x[10];
my ($a, $b, $c);
my $c='A';
my @x;
my %h;
聲明使用my標示
表示值的變量以$開頭,表示數組的變量以@開頭,表示哈希表的變量以%開頭。
聲明可以省略(不建議)
字符串
char* h1=”hello\n”;
char* h2=”hello\\n”;
$h1=”hello\n”;
$h2=’hello\n’;
雙引號解釋內部的\n,而單引號則不解釋
一維數組
int arr[10];
arr[0]=0;
for(i=0;i<10;i++)
arr[i]=i;
my @arr;
$arr[0]=0;
@arr[3..5]=(3..5);
數組聲明以@標示
動態數組,不需要指定大小
數組下標從0開始
訪問數組元素值的時候,要以$開頭表示訪問的是數值
[3..5]表示數組中下標為3到5之間的元素組成的數組
數組之間可以直接賦值
多維數組
int arr[10][10];
arr[0][1]=9;
my @arr;
$arr[0][1]=9;
Perl並不直接支持多維數組,而是以數組引用的方式間接支持。例如arr[0]的內容就是一個數組的引用地址。
指針
char c;
int* x=&c;
c='a';
printf(*x);
my $c;
my $x=\$c;
$c='a';
print $x;
\和C中的&類似,意思是取引用
void hello() {
printf(“Hello\n”);
}
void (*hi)()=hello;
(*p)();
sub hello{
print "Hello\n";
}
my $hi = *hello;
&$hi;
&表示調用函數
*取函數的代碼地址
不必用括號把參數括起來
調用時的括號也是可選的
條件語句
if (x>0) x=0;
x>0 ? x=0 : ;
if ($x>0) { $x=0; }
$x=0 if $x>0;
$x=0 unless $x<=0;
$x>0 ? $x=0 : ;
if 結構可以反轉,意義不變,注意前句沒有分號。
顧名思義, unless是“除非”的意思。這裡的四個表達方式是等價的。注意第一種方式中,條件部分的圓括號和語句部分的花括號是不可省略的。
循環語句
略
foreach (@arry)
foreach my $key(@ary)
foreach $count (1..10)
for/while的語法都和C類似。
foreach關鍵字也可以用for,意義不變。
函數
int max(int x, int y)
{
return x>y?x:y;
}
int n=max(1,2);
sub max
{
my ($x, $y)=@_;
return $x>$y?$x:$y;
}
my $n=max(1,2)
注意下劃線”_”也是一個合法的變量名。而@_是Perl內置的一個數組,內容為函數的參數。
my ($x, $y) 表示聲明了一個有兩個元素的數組,並將兩個元素映射到$x和$y上。
($x,$y)=@_;則表示兩個數組之間的復制,@_中對應的元素的值就賦值給了$x和$y.這是一個簡便的寫法,也可以這樣寫
my $x=$_[0]; my $y=$_[1];
return是可選的,默認返回最後一個表達式的值
語法約束
1. 編譯時打開編譯器所有的警告選項
2. 使用lint工具
3. perl –w myprogram.pl 打開運行警告開關,如果運行時Perl檢查到了可能的錯誤,會顯示警告信息,否則它默認是什麼也不提示繼續執行。
4. #!/usr/bin/perl –w 在代碼文件第一行中加入-w選項開關
5. use strict; 使用嚴格語法約束
運行
編譯後直接執行
1. perl myprogram.pl 手工執行
2. #!/usr/bin/perl
Unix下在代碼第一行加入,然後給文件加上可執行的屬性 chmod +x myprogram.pl,之後就可以用./myprogram.pl命令來運行。
3. Windows下,安裝ActivePerl的時候,已經將.pl後綴的文件和perl的解釋程序關聯起來了,因此直接雙擊文件圖標就可以運行。
需要說明的是,在Perl的世界中有一句名言“條條大路通羅馬”, 這句話的意思是說同樣一件事情Perl允許你用很多種不同的方式去做。因此上表的例子風格是按照C的習慣來寫的,並且為了簡化起見,只是挑選了與C相似的內容。事實上,Perl包含了很多C沒有的東西,例如內置的Hash表、隊列、正則表達式、格式定義等等。
從框架開始
Perl有很多表達方式,我們可以選擇一種自己熟悉、容易理解的方式來寫Perl的程序。例如,你是一個經驗豐富的C程序員,那麼你可以選擇以C的風格來寫Perl程序。下面是一個小小的樣板框架
#!/usr/bin/perl -w
use strict;
# 程序開始的第一行語句,調用main函數
main();
# 定義main函數
sub main
{
…
}
在這個框架下面,你幾乎可以容易就開始你的Perl開發了。如果需要處理命令行參數,就可以稍微的擴展一下這個框架。
#!/usr/bin/perl -w
use strict;
use Getopt::Std;
main();
my $configfile;
sub ProcessOptions
{
my $VERSION = '1.0.0';
my $USAGE = "pp.pl [-v | -c configfile]\n";
my $opts={};
dIE $USAGE unless( getopts("c:v", $opts) );
dIE $VERSION if ($opts->{'v'});
$configfile=$opts->{'c'} ? $opts->{'c'} : 'config.XML' ;
}
sub main
{
ProcessOptions();
print $configfile;
…
}
實際上,剩余的工作和以往的工作差不多了,編寫一個一個的函數,並實現你的業務邏輯。對於你這樣一個聰明的程序員來說,學會Perl是一個很容易的事情。
常見問題
以我的學習經驗來看,在開發的過程中可能有一些常用但是很分散的細節問題會讓你感到困惑。
1、 程序的入口參數怎麼取?
內置數組@ARGV包含了所有的運行參數。可以打印出來看看 print @ARGV;
2、 函數如何傳參數、取參數?
每個函數內部都有一個內置的數組 @_ ,這個數組的元素就是函數的參數。例如傳入的第一個參數就是$_[0],第二個是$_[1]。唔,如你所見,Perl的函數參數就是C中的動態參數。
3、 默認變量是什麼
這個可能會把你的頭搞暈。有一個內置變量 $_ ,
4、 顯示消息、退出常見的簡單寫法
dIE ‘Error on program’;
也可以在條件不滿足的情況下使用
dIE ‘Configuration error’ unless($doc->getDocumentElement);
5、 格式化輸出
可以用簡單的print語句進行一般的輸出操作,如果需要復雜的格式化輸出,可以使用printf語句……跟C的用法幾乎是一樣的。
printf("pi=%.6f", 355/113);
6、 =>是什麼東西?
在使用Hash表的時候,可以經常看到=>這個符號。例如這樣的一個定義:
my $account={
'Simon'=> '[email protected]',
'Jesse'=> '[email protected]'
};
其實,=>符號跟逗號”,”是等價的。Perl裡面的Hash表事實上是一個數組,只是把奇數位元素看做是Key(鍵),而把偶數位的元素看做是Value(值)。
7、 關於引用的一點說明
Perl的引用類似C的指針,所謂的引用事實上就是地址。取一個變量的地址用反斜槓”\”操作符,例如 $p=\$x; 那麼$p就是一個指向$x變量的指針。要引用指針的值,使用”$”操作符,例如 print $$p; 就是打印$x的值。
引用不單單可以引用變量,也可以引用數組、HASH表、函數,取函數的地址可以使用*操作符。
還能做什麼
Perl作為一個功能強大的腳本語言,可以應用在Web 編程、數據庫、XML、系統管理、圖形圖像、自然語言、壓縮、加密、郵件系統、軟件測試等各個地方。在CPAN上,你可以找到各種各樣你所需要的模塊支持。例如,你可以:
編寫系統管理的腳本
和apache結合起來,編寫CGI程序
編寫動態網頁
使用Net命名空間下的類編寫網絡應用程序
使用Authen::Captcha模塊實現提交時的驗證碼的功能
使用Storable模塊處理Perl的各種數據結構
使用GD/Image::MagicK模塊處理圖形
等等…