Perl 內建的 grep 函式就像過濾器一樣。你可以給它一個串列以及一個要過濾的條件,它會回傳一個符回過濾條件的串列。 它是 UNIX和 Linux 指令中 grep 或是 egrep 的通式,不過我們不必真的去了解這些指令。

grep 函式需要兩個參數:一個程式區塊和一個串列。

串列中的每個值都會被傳到 $_裡, Perl 的預設變數, 然後這個程式區塊就會被執行。如果程式區塊回傳值為 false,那麼這個串列值就會被丟棄。如果程式區塊回傳值為 true, 那麼這個串列值就會被保留並且當作回傳值。

請注意,在程式區塊和串列之間並沒有逗號。

讓我們來看看幾個例子:

過濾數字

my @numbers = qw(8 2 5 3 1 7);
my @big_numbers = grep { $_ > 4 } @numbers;
print "@big_numbers\n";      # (8, 5, 7)

這裡的 grep 函式回傳大於 4 的值,並且把小於 4 的值給丟棄。

過濾檔案

my @files = glob "*.log";
my @old_files = grep { -M $_ > 365 } @files;
print join "\n", @old_files;

glob "*.log" 會回傳所有在現在目錄中的 .log 檔案。

-M $path_to_file 會回傳這個檔案最後被變動的時間跟現在時間相差的天數。

這個例子就是要找出最後變動時間超過一年以上的檔案。

找出某個元素是否位在陣列中?

grep 也可以使用在找出某個元素是否位在陣列中。比如,你有一組串列的名字,妳想要知道裏面是否有某個名字?

use strict;
use warnings;

my @names = qw(Foo Bar Baz);
my $visitor = <STDIN>;
chomp $visitor;
if (grep { $visitor eq $_ } @names) {
   print "Visitor $visitor is in the guest list\n";
} else {
   print "Visitor $visitor is NOT in the guest list\n";
}

這個例子中,我們把 grep 放在 SCALAR 意境中。 在 SCALAR 意境中, grep 會傳回符合元素字串的數目。如果回傳 0, 代表這個表示式為 false。如果回傳正值,代表這個表示式為 true。

這個方法可以運作是因為它利用了意境這個方式,但是有人可能對這個比較陌生。讓我們看看利用 any 函式,位在 List::MoreUtils 模組中,也可以解決這個問題。

是否有任何元素配對成功?

any 這個函式跟 grep 有同樣的語法: 一個程式區塊和一個串列,但是它只回傳 true 或是 false。如果程式區塊配對成功就回傳 true,反之則回傳 false。 而且它有短捷徑,所以在大量串列時,它能夠運算的很快。

use List::MoreUtils qw(any);
if (any { $visitor eq $_ } @names) {
   print "Visitor $visitor is in the guest list\n";
} else {
   print "Visitor $visitor is NOT in the guest list\n";
}

UNIX 和 Linux grep?

解釋如下:

我曾經提過 Perl 的 grep 是 UNIX和 Linux 指令中 grep 或是 egrep 的通式。

UNIX grep 是基於正規表達式來過濾檔案中的每一列。

Perl's grep 則是可以用在任何表達式來過濾任何串列的值。

下面這個程式可以代表 UNIX grep 的基本功能:

my $regex = shift;
print grep { $_ =~ /$regex/ } <>;

第一行從命令列得到第一個參數來當作正規表達式。剩下的命令列參數則應該是檔案名稱。

鑽石符號 <> 可以讀取命令列中檔案名稱的每一列。接下來 grep 根據正規表達式來過濾每一列。每一列只要能成功配對的話,就會被印出來。

Windows 上的 grep

Windows 上並沒有 grep 這樣的工具,不過你可以安裝一個或是你可以使用上面的 perl 程式腳本。