cv.cm

編碼 · 程式語言

Perl 入門教學

閱讀約 9 分鐘

簡答

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 的規則運算式風格。

全部編碼教學