編碼 · 程式語言
Perl 入門教學
簡答
Perl 是 Larry Wall 於 1987 年創造的動態腳本語言,以文字處理與強大的內建規則運算式聞名。大多數 Unix 系統都預先安裝了它,用於系統管理、日誌處理與生物資訊。本頁涵蓋執行 Perl、純量、陣列與雜湊、流程控制、副程式、參照、規則運算式,以及用 die 和 eval 處理錯誤。
開啟正規測試Perl 是什麼、可以拿來做什麼
Perl 是 Larry Wall 於 1987 年創造的高階動態型別語言。它為文字處理與報表產生而設計,後來成為 Unix 系統管理與早期網頁開發的「膠水語言」。Perl 的規則運算式影響深遠,許多其他語言都採用了「相容於 Perl」的正規表示式語法。
如今 Perl 用於系統管理腳本、日誌與文字處理、生物資訊、舊有的網頁應用程式,以及單行命令列過濾器。大多數 Linux 與 Unix 系統都預先安裝了它。CPAN(Comprehensive Perl Archive Network)收錄了大量可重複使用的模組。本教學涵蓋 Perl 5;曾被稱為 Perl 6 的語言現在是獨立的語言,名為 Raku。
執行 Hello, world
用 perl -v 確認 Perl 已安裝。在 Windows 上,Strawberry Perl 是常見的發行版。每個腳本都請以 use strict; 和 use warnings; 開頭:它們能抓出打字錯誤與不安全的程式碼。
#!/usr/bin/env perl
use strict;
use warnings;
print "Hello, world!\n";perl hello.pl
# Hello, world!
perl -e 'print "Hello from a one-liner\n"'陳述式以分號結尾。註解以 # 開頭。雙引號字串會插入變數與 \n 等跳脫字元;單引號字串則是字面值。
變數:純量、陣列與雜湊
Perl 變數以符號(sigil)開頭,表示值的種類。每個變數都用 my 宣告,使其限於所在區塊內。
use strict;
use warnings;
my $name = "Perl"; # scalar: one value
my $year = 1987;
my @langs = ("Perl", "C", "sh"); # array: ordered list
my %port = (http => 80, https => 443); # hash: key-value pairs
print "$name since $year\n"; # Perl since 1987
print "First: $langs[0]\n"; # First: Perl (element is a scalar, so $)
print "Count: ", scalar(@langs), "\n"; # Count: 3
print "HTTPS: $port{https}\n"; # HTTPS: 443
print "Sum: ", "10" + 5, "\n"; # Sum: 15 (strings convert to numbers)
print "Join: " . "a" . "b" x 3, "\n"; # Join: abbb| 符號 | 種類 | 範例 |
|---|---|---|
$ | 純量:數字、字串或參照 | my $n = 42; |
@ | 陣列:有順序的純量清單 | my @list = (1, 2); |
% | 雜湊:無順序的鍵值對 | my %h = (a => 1); |
& | 副程式(很少這樣寫) | &name |
流程控制
Perl 有 if、elsif、else、unless、while、until、for 和 foreach。簡短的陳述式後面可以接條件,讀起來像英文。next 跳到下一次迭代,last 離開迴圈。
use strict;
use warnings;
my $score = 72;
if ($score >= 90) {
print "A\n";
} elsif ($score >= 70) {
print "B\n"; # B
} else {
print "C\n";
}
print "passed\n" if $score >= 50; # statement modifier
print "not perfect\n" unless $score == 100;
for my $i (1 .. 3) {
print "i = $i\n";
}
my @names = ("ann", "bo");
foreach my $n (@names) {
next if $n eq "bo"; # skip to the next item
print ucfirst($n), "\n"; # Ann
}
my $k = 3;
while ($k > 0) { $k--; }假值有 0、"0"、空字串 ""、空清單與 undef。其餘一律為真。
副程式
用 sub 定義函式。引數會放進特殊陣列 @_,慣例的第一行會把它們複製到具名變數中。副程式可以回傳純量或清單。
use strict;
use warnings;
sub add {
my ($x, $y) = @_; # arguments arrive in @_
return $x + $y;
}
sub greet {
my ($name, $greeting) = @_;
$greeting //= "Hello"; # default if undefined
return "$greeting, $name!";
}
sub stats {
my @nums = @_;
my $sum = 0;
$sum += $_ for @nums;
return ($sum, $sum / @nums); # return a list
}
print add(2, 3), "\n"; # 5
print greet("Ann"), "\n"; # Hello, Ann!
my ($total, $avg) = stats(4, 8, 6);
print "$total $avg\n"; # 18 6現代 Perl 也支援簽章,例如 sub add($x, $y) { ... },需使用 use v5.36; 或更新版本啟用,同時也會開啟 strict 和 warnings。
陣列、雜湊與參照
陣列和雜湊附有強大的內建函式,例如 push、pop、shift、sort、map、grep、keys 和 exists。巢狀資料使用參照:[ ... ] 建立陣列參照,{ ... } 建立雜湊參照,-> 則用來取用它們。
use strict;
use warnings;
my @nums = (5, 3, 8, 1);
push @nums, 9; # add to the end
my $last = pop @nums; # remove from the end: 9
my @sorted = sort { $a <=> $b } @nums; # numeric sort
my @doubled = map { $_ * 2 } @sorted;
my @big = grep { $_ > 4 } @sorted;
print "@sorted | @doubled | @big\n"; # 1 3 5 8 | 2 6 10 16 | 5 8
my %age = (ann => 31, bo => 25);
$age{cy} = 40;
delete $age{bo};
for my $who (sort keys %age) {
print "$who is $age{$who}\n"; # ann is 31 / cy is 40
}
print "has ann\n" if exists $age{ann};
# references build nested data structures
my $user = { name => "Ann", langs => ["Perl", "SQL"] };
print "$user->{name} knows $user->{langs}[1]\n"; # Ann knows SQL
print scalar @{ $user->{langs} }, "\n"; # 2在 sort 區塊內,$a 和 $b 是正在比較的兩個項目。<=> 以數值比較,cmp 以字串比較;單純的 sort @list 以字串排序,所以 10 會排在 9 前面。
規則運算式
規則運算式內建於 Perl 的語法中,這是它的招牌特色。=~ 套用比對 m/.../ 或取代 s/.../.../。擷取的群組可用 $1、$2 等取得,g(全域)和 i(不分大小寫)等修飾字元放在結尾的斜線之後。
use strict;
use warnings;
my $line = "2026-10-31 ERROR disk full on /dev/sda1";
if ($line =~ /^(\d{4})-(\d\d)-(\d\d)\s+(\w+)/) {
print "year=$1 level=$4\n"; # year=2026 level=ERROR
}
(my $masked = $line) =~ s/\d/#/g; # copy, then replace digits
print "$masked\n"; # ####-##-## ERROR disk full on /dev/sda#
my @words = split /\s+/, $line;
print scalar(@words), " words\n"; # 6 words
my %h = ("a=1, b=2, c=3" =~ /(\w)=(\d)/g); # pairs into a hash
print join(",", map { "$_$h{$_}" } sort keys %h), "\n"; # a1,b2,c3用 qr/.../ 可把編譯過的樣式存進變數,用 x 修飾字元則可把長樣式分成多行並加上註解。
錯誤處理
die 拋出例外,warn 把警告印到標準錯誤。eval { ... } 捕捉例外並把錯誤訊息放進 $@。open 等系統呼叫在失敗時回傳假值,並把 $! 設為作業系統的錯誤,所以請用 or die 檢查。
use strict;
use warnings;
sub divide {
my ($x, $y) = @_;
die "division by zero\n" if $y == 0;
return $x / $y;
}
my $result = eval { divide(1, 0) };
if ($@) {
print "Caught: $@"; # Caught: division by zero
}
open(my $fh, "<", "missing.txt")
or print "Cannot open: $!\n"; # Cannot open: No such file or directory以 \n 結尾的 die 訊息會照原樣印出;沒有換行時,Perl 會附加檔名與行號。自 Perl 5.34 起還有實驗性的 try/catch 功能,Try::Tiny 等模組也被廣泛使用。
完整的小程式:日誌等級計數器
這個腳本從命令列指定的檔案或標準輸入讀取日誌,依等級統計行數,並印出排序後的摘要。它示範了菱形運算子 <>、規則運算式、雜湊與 printf。
#!/usr/bin/env perl
# logstat.pl: count log levels in a file or standard input
use strict;
use warnings;
my %count;
my $lines = 0;
while (my $line = <>) { # reads files named on the command line, or STDIN
chomp $line;
$lines++;
next unless $line =~ /\b(INFO|WARN|ERROR)\b/;
$count{$1}++;
}
printf "%d lines read\n", $lines;
for my $level (sort { $count{$b} <=> $count{$a} || $a cmp $b } keys %count) {
printf "%-5s %3d\n", $level, $count{$level};
}perl logstat.pl app.log
# 120 lines read
# INFO 97
# WARN 18
# ERROR 5接下來可以學什麼
- perldoc.perl.org 上的 perlintro 與其他教學,也可以用
perldoc perlintro離線閱讀。 - Learning Perl 與 Modern Perl,兩本知名的初學者書籍。
- MetaCPAN,CPAN 模組的搜尋介面。
- perlre 與 perlretut,完整的規則運算式參考。
常見問題
Perl 現在還有人用嗎?
有。大多數類 Unix 系統都預先安裝了 Perl,它在系統管理、文字處理、生物資訊與長期運作的商業系統中仍然很常見,不過以 Perl 開始的新網頁專案已經變少。
Perl 和 Raku 有什麼差別?
Raku 起初是 Perl 6,後來成為設計不同的獨立語言。Perl 5 則繼續獨立發展,Perl 這個名稱指的就是它。
為什麼 Perl 變數以 $、@ 和 % 開頭?
符號顯示你得到的值的種類:$ 代表單一純量,@ 代表清單,% 代表雜湊。這就是為什麼 @items 的一個元素要寫成 $items[0]。
Perl 的 chomp 有什麼作用?
chomp 會移除字串結尾的換行字元,從檔案或鍵盤讀取一行後通常都需要它。
Perl 適合處理規則運算式嗎?
適合。規則運算式是 Perl 核心語法的一部分,許多其他工具和語言使用的 PCRE 函式庫,其名稱就來自 Perl 的規則運算式風格。