הפונקציה grep , שהיא פונקציה מובנית של פרל (Perl) היא פונקציית סינון. מעבירים לפונקציה רשימת ערכים ותנאי, והיא מחזירה תת-רשימה של הערכים שמקיימים את התנאי. זו היא למעשה הכללה של הפקודות grep ו-egrep שמוכרות לנו מ-Unix ומלינוקס, אך אין צורך להכיר את הפקודות כדי להבין את פונקציית ה-grep של פרל.

הפונקציה grep מקבלת שני ארגומנטים. בלוק ורשימת ערכים.

עבור כל ערך ברשימה הערך מוצב ב- $_, משתנה ברירת המחדל הסקלרי של פרל, ואז הקוד בבלוק מבוצע. אם בלוק הקוד מחזיר false, הערך הנבדק מושמט. אם בלוק הקוד מחזיר true אז הערך הנבדק מהרשימה נשמר כאחד הערכים המוחזרים.

שימו לב, אין פסיק בין בלוק הקוד לבין הפרמטר השני!

נראה מספר דוגמאות לשימוש ב- grep:

סינון מספרים קטנים

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 בהקשר סקלרי. בהקשר סקלריgrep מחזירה את מספר הערכים שעברו את הסינון. כיוון שאנחנו בודקים אם $visitor שווה לאלמנט הנוכחי, קריאה זו grep תחזיר את מספר הפעמים שמקרה זה מתקיים.

אם המספר הוא 0, הביטוי כולו מקבל את הערך אם מספר הפעמים הוא מספר חיובי כלשהו, אז ערך הביטוי הוא true.

הפתרון עובד, אך כיוון שהוא מסתמך על הקשר ייתכן שהוא לא יהיה ברור לכולם. הנה פתרון נוסף שמשתמש בפונקציה any של המודול List::MoreUtils .

האם קיים אלמנט כלשהו שמתאים?

בדומה ל-grep גם any מקבלת בלוק של קוד ורשימת ערכים, אך היא מחזירה רק true או false. True, אם הבלוק מחזיר ערך 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";
}

grep ביוניקס ו- grep בלינוקס?

להשלמת ההסבר:

ציינתי קודם שהפונקציה המובנית grep בפרל היא הכללה של של פקודת ה grep ביוניקס.

פקודת ה- grep ביוניקס מסננת את השורות על סמך ביטוי רגולרי.

grep בפרל יכולה לסנן לפי כל תנאי.

הקוד הבא בפרל מיישם את הגירסה הבסיסת של grep ביוניקס:

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

השורה הראשונה מקבל את הארגומנט משורת הפקודה, והארגומנט צריך להיות ביטוי רגולרי. שאר הארגומטים בשורת הפקודה צריכים להיות שמות קבצים.

אופרטור היהלום (diamond) <> קורא את השורת מכל הקבצים על שורת הפקודה. הפונקציה מסננת אותם לפי הביטוי הרגולרי. השורות שעוברות את הסינון מודפסות.

grep ב-Windows

ב-Windows אין פקודת grep מובנית, אבל ניתן להתקין grep או להשתמש בקוד פרל כמו הקוד הנ"ל.