מ־shell ל־Perl#

אתם מכירים את ה־shell. אתם מדביקים תוכניות יחד עם צינורות, נשענים על sed, awk, grep, ו־cut בשרשרת צינורות בלי לחשוב, לוכדים פלט פקודה עם $(...), וכותבים זרימת בקרה עם for f in *, while read line, ו־case. מדריך זה ממפה כל אחד מההרגלים הללו אל Perl, כך שתוכלו לשמר את רפלקסי שרשרת הצינורות ולאמץ את הדברים שה־shell עושה למכאיבים.

כשסקריפט shell גדל מעבר לכ־50 שורות של ציטוט ושרשראות sed/awk/grep, Perl היא הצעד הטבעי הבא: היא עושה את כל זה בשפה אחת, עם מבני נתונים אמיתיים במקום מחרוזות מופרדות־רווח. המסגור שכדאי לקחת איתכם: Perl היא שרשרת הצינורות של ה־shell, אבל שפת המסנן היא שפה אמיתית. אתם עדיין open צינורות, עדיין מבצעים glob, עדיין מריצים פקודות וקוראים את פלטן. מה שמשתנה הוא שבין הצינורות יש לכם עכשיו @arrays, %hashes, ביטויים רגולריים אמיתיים, וחשבון שאינו זקוק ל־$(( )). טיפוס הנתונים האחד של ה־shell הוא המחרוזת; Perl נותנת לכם מכלים ממשיים.

Every runnable example here was executed on pperl, a Rust reimplementation of Perl 5.42; the # ... comments show its real output. Start scripts with:

use v5.42;

שורה אחת זו מפעילה את strict, את warnings, ואת הפונקציה המובנית say (כלומר print עם תו שורה חדשה נגרר), מערך התכונות המודרני המשמש לאורך כל הדרך.

הערה

מתגי שורה־אחת ב־build הזה של pperl התאום הטבעי ב־Perl של מפתח ה־shell הוא שורת־הקוד־האחת: perl -ne ו־perl -pe, המסנן שבתוך־השרשרת. מתגים אלה הם perl5 5.42 תקני ומוצגים לאורך מדריך זה כצורה האידיומטית. אך משפחת מתגי הלולאה המשתמעת (-n, -p, -a, -F, -i, -l) עדיין אינה מזוהה על ידי build זה של pperl - רק -e מזוהה. לכן דוגמאות שורת־הקוד־האחת שלהלן מוצגות ללא פלט, וכל אחת מהן מצומדת לצורת סקריפט־מלא עם לולאה מפורשת (while (<$fh>) { ... }) שאכן רצה על pperl היום ונושאת את הפלט המאומת. השתמשו בצורת הסקריפט המלא עד שהמתגים ייכנסו.

כיצד לקרוא את זה#

לולאת הרשומה#

הניב שורה־בכל־פעם של ה־shell הוא while read line; do ... done, הקורא שורה אחת בכל איטרציה אל $line (ומקצץ אותה בהתאם ל־IFS). המקבילה של Perl היא while (<$fh>), הקוראת שורה אחת בכל איטרציה אל $_, משתנה הנושא המשתמע. ההבדל המכריע: Perl שומרת על תו השורה החדשה הנגרר, כך שאתם chomp אותו בעצמכם, בדיוק היכן שה־read של ה־shell היה אוכל אותו.

while read line; do
    echo "got: $line"
done < data.txt
use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (my $line = <$fh>) {     # each line, with its newline
    chomp $line;               # strip it, like read does
    say "got: $line";
}
close $fh;
# got: apple
# got: banana
# got: cherry

(קובץ הקלט החזיק apple, banana, cherry.) קראו עם ה־<$fh> החשוף והשורה נוחתת ב־$_, ברירת המחדל שעליה פועלות רוב הפונקציות המובנות לשורות. מספר השורה נמצא ב־$.. זו הצורה שכדאי להישען עליה כשאתם מתרגמים מסנן:

use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (<$fh>) {                # line lands in $_
    chomp;                     # chomp $_ by default
    say "$. : $_";             # $. is the line number
}
close $fh;
# 1 : apple
# 2 : banana
# 3 : cherry

המלכודת: read מפצל על IFS ומקצץ; <$fh> אינו עושה אף אחד מהם. קריאת שורה ב־Perl היא השורה הגולמית כולה כולל ה־\n, כך ש־chomp הוא הרפלקס שלכם, ופיצול־שדות הוא שלב נפרד ומפורש (הסעיף הבא). מה שאתם מאבדים בקיצוץ אוטומטי אתם מרוויחים באי־מתן ל־IFS לרסק את הנתונים שלכם בשקט.

שדות ופיצול#

ב־shell אתם מגיעים לשדות עם cut -d: -f1, עם awk '{print $1}', או על ידי קביעת IFS ומתן ל־read לפצל עבורכם. התשובה האחת של Perl היא split: תנו לו מפריד ומחרוזת, קבלו בחזרה מערך אמיתי. שדות מיקומיים שה־shell ממספר $1, $2 הופכים לאיברי מערך $f[0], $f[1] - מבוססי־0, בניגוד ל־$1 המבוסס־1 של awk.

line='root:x:0:0:root:/root:/bin/bash'
user=$(echo "$line" | cut -d: -f1)
shell=$(echo "$line" | cut -d: -f7)
echo "$user $shell"
use v5.42;
my $line = "root:x:0:0:root:/root:/bin/bash";
my @f = split /:/, $line;
say "user: $f[0]";            # user: root
say "shell: $f[-1]";          # shell: /bin/bash   (negative index)
say "fields: ", scalar @f;    # fields: 7

כדי לפצל על רצפים של רווחים לבנים - פיצול־המילים של ברירת המחדל של ה־shell, וברירת המחדל של awk - השתמשו בתבנית המיוחדת split ' ' (מחרוזת בעלת רווח־בודד מילולי, המדלגת גם על רווחים מובילים). כדי לחבר שדות בחזרה, join:

use v5.42;
my @words = split ' ', "  the   quick brown  ";
say scalar @words;            # 3   (leading/inner blanks collapsed)
say join "|", @words;         # the|quick|brown

המלכודת: split /:/ עם תבנית מילולית שומר שדות ריקים נגררים רק עד גבול מסוים - כברירת מחדל Perl משמיטה שדות ריקים נגררים, כך ש־split /:/, "a::" מניב ("a"), לא ("a", "", ""). העבירו גבול שלילי (split /:/, $s, -1) כדי לשמרם, המקבילה לאכפתיות משדות cut ריקים נגררים. ו־split ' ' (צורת הרווח־הלבן־הקסום) אינה זהה ל־split / / (רווח מילולי בודד): הראשונה דוחסת רצפים ומקצצת, השנייה אינה עושה זאת.

ביטויים רגולריים והחלפה#

הרחבות הפרמטרים ${} של ה־shell הופכות לביטויים רגולריים אמיתיים - וזו שדרוג גדול. ${var#prefix}, ${var%suffix}, ${var/a/b}, ו־${var//a/b} הם כל אחד עריכת מחרוזת מוגבלת ולמטרה מיוחדת. Perl מחליפה את כל המשפחה באופרטור אחד, s///, המקבל ביטוי רגולרי ממשי ואת מערך הדגלים המלא.

f='report.txt.bak'
base=${f%.bak}              # strip suffix
path='/usr/local/bin'
tail=${path##*/}            # basename
dir=${path%/*}              # dirname
s='a-b-c'
one=${s/-/_}                # replace first
all=${s//-/_}               # replace all
use v5.42;
my $f = "report.txt.bak";
(my $base = $f) =~ s/\.bak$//;       # ${f%.bak}
say $base;                           # report.txt

my $path = "/usr/local/bin";
(my $tail = $path) =~ s{.*/}{};      # ${path##*/}  basename
say $tail;                           # bin
(my $dir = $path) =~ s{/[^/]*$}{};   # ${path%/*}   dirname
say $dir;                            # /usr/local

my $s = "a-b-c";
(my $one = $s) =~ s/-/_/;            # ${s/-/_}   first only
say $one;                            # a_b-c
(my $all = $s) =~ s/-/_/g;           # ${s//-/_}  /g = global
say $all;                            # a_b_c

מכיוון שזה ביטוי רגולרי אמיתי, הדברים שה־shell אינו יכול לעשות כלל הם טריוויאליים: קבוצות לכידה נוחתות ב־$1, $2; הדגל /e מריץ קוד Perl בהחלפה; הדגל /r מחזיר עותק במקום לערוך במקום; ו־/i, /x משנים את ההתאמה.

use v5.42;
my $s = "items: 3 and 4";
(my $t = $s) =~ s/(\d+)/$1 * 10/ge;  # /e: replacement is Perl code
say $t;                              # items: 30 and 40

my $date = "2026-06-21";
my $us = $date =~ s{(\d+)-(\d+)-(\d+)}{$2/$3/$1}r;  # /r: return a copy
say $us;                             # 06/21/2026
say $date;                           # 2026-06-21   (unchanged)

המלכודת: s/// חשוף עורך את היעד שלו ומחזיר את ספירת ההחלפות, לא את המחרוזת החדשה - כך שהדוגמאות מעתיקות תחילה עם (my $t = $s) =~ ... או משתמשות בדגל /r. כתיבת my $new = ($s =~ s/a/b/) שמה מספר ב־$new. זו הפתעת היום־הראשון הנפוצה ביותר. שימו לב גם שהמפרידים חופשיים: s{...}{...} ו־s#...#...# חוסכים מכם הוספת לוכסן הפוך ללוכסנים בתבנית נתיב, באותו אופן שבו ה־${path##*/} של ה־shell כבר נמנע מהם.

כתובות וטווחים#

בחירת שורות לפי מספר או תבנית - grep, grep -n, awk 'NR==2', sed -n '/a/,/b/p' - הופכת ל־if בתחביר סיומת על תנאי בתוך הלולאה. מספר השורה הוא $.; בדיקת התבנית היא /pat/ כנגד $_.

grep -n an data.txt              # number + matching lines
awk 'NR==2' data.txt             # the second line
use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (<$fh>) {
    chomp;
    say "$. : $_" if /an/;       # grep -n 'an'
}
close $fh;
# 2 : banana

טווח שורות - בורר ה־/start/,/end/ של sed/awk - הופך לאופרטור הטווח flip-flop של Perl, /start/ .. /end/, אמת מהשורה התואמת את התבנית השמאלית עד השורה התואמת את הימנית, כולל:

use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (<$fh>) {
    print if /banana/ .. /cherry/;   # the /start/,/end/ range
}
close $fh;
# banana
# cherry

המלכודת: בבדיקה בוליאנית .. הוא אופרטור ה־flip-flop (טווח), לא אופרטור טווח־הרשימה (שהוא 1 .. 10 הבונה רשימה). Perl מבחינה ביניהם לפי הקשר - בעמדה הבוליאנית של if, .. הוא בורר טווח־השורות בעל־המצב; בהקשר רשימה הוא בונה רצף. אותן שתי נקודות, מוכרעות לפי היכן שאתם כותבים אותן.

קלט/פלט, צינורות, ועריכה במקום#

זה הלב של המדריך, מכיוון שזה הלב של כתיבת סקריפטים ב־shell. לכל מה שאתם עושים עם |, >, >>, 2>&1, <<EOF, ו־$(...) יש איות Perl ישיר, וברגע שסקריפט גדל מעבר לשרשרת צינורות יחידה זה הסעיף שמשתלם.

החלפת פקודה. ה־$(cmd) והגרשים האחוריים של ה־shell הופכים לגרשים האחוריים או ל־qx{...} של Perl. הלכידה רגישת־הקשר: בהקשר סקלרי אתם מקבלים את כל הפלט כמחרוזת אחת; בהקשר רשימה אתם מקבלים איבר אחד לכל שורה.

out=$(echo hello)
mapfile -t lines < <(seq 1 3)
use v5.42;
my $out = `echo hello`;           # scalar: whole output, one string
chomp $out;
say "got: $out";                  # got: hello

my @lines = qx{seq 1 3};          # list: one element per line
chomp @lines;
say "lines: @lines";              # lines: 1 2 3

כשאינכם זקוקים לפלט, system() מריץ פקודה ונותן לכם את סטטוס היציאה שלה (ראו זרימת בקרה עבור $?). העבירו ל־system רשימה ושום shell אינו מעורב - הצורה הבטוחה הנמנעת מגיהינום הציטוט:

use v5.42;
system('mkdir', '-p', '/tmp/sh_demo');   # list form: no shell, no quoting
say "made it" if -d '/tmp/sh_demo';      # made it

צינורות. שלב בשרשרת צינורות - קריאת פלט פקודה או הזנת קלט פקודה - הוא open עם מצב צינור. open my $fh, '-|', LIST קורא את ה־stdout של פקודה; open my $fh, '|-', LIST כותב ל־stdin של פקודה. צורת הרשימה (פקודה בתוספת ארגומנטים כמחרוזות נפרדות) אינה מריצה shell, כך שאין דבר לצטט.

seq 1 3 | while read n; do echo "n=$n"; done    # read from a command
printf '%s\n' banana apple cherry | sort         # write to a command
use v5.42;
open my $ph, '-|', 'seq', '1', '3' or die "pipe: $!";  # read command output
my @nums = <$ph>;
close $ph;
chomp @nums;
say "got: @nums";                 # got: 1 2 3
use v5.42;
open my $sort, '|-', 'sort' or die "pipe: $!";  # write to command stdin
print {$sort} "$_\n" for qw(banana apple cherry);
close $sort;
# apple
# banana
# cherry

הפניה־מחדש. > ו־>> הם מצבי ה־open '>' (קיטום) ו־'>>' (הוספה); open תלת־ארגומנטי עם מטפל מילוני הוא הניב. print {$fh} ... כותב למטפל. כדי למזג את ה־stderr של תהליך־בן לתוך הזרם הנלכד (2>&1), שמרו את ההפניה־מחדש בתוך הפקודה שאתם מוסרים לגרשים האחוריים:

use v5.42;
open my $fh, '>', '/tmp/sh_out.txt' or die "write: $!";
print {$fh} "line $_\n" for 1 .. 3;
close $fh;

my $both = `sh -c 'echo to-out; echo to-err 1>&2' 2>&1`;  # merge stderr
print $both;
# to-out
# to-err

Here-docs. ה־<<EOF (מבצע אינטרפולציה) וה־<<'EOF' (מילולי) של ה־shell ממופים חד־חד־ערכית ל־<<"END" ו־<<'END' של Perl. גרשיים כפולים על המסיים מבצעים אינטרפולציה למשתנים; גרשיים בודדים אינם.

use v5.42;
my $name = "world";
print <<"END";                # interpolating, like <<EOF
hello $name
END
print <<'END';                # literal, like <<'EOF'
no $interpolation here
END
# hello world
# no $interpolation here

עריכה במקום. sed -i כותב קובץ מחדש במקום; ה־Perl האידיומטית היא אותו מתג על שורת־קוד־אחת של -p. המקבילה הניתנת־להרצה היום היא קריאה־שינוי־כתיבה מפורשת ש־-i מבצע מתחת למכסה המנוע - בלעו, המירו, כתבו חזרה:

use v5.42;
my $path = '/tmp/inplace_data.txt';   # holds: red green blue

open my $in, '<', $path or die "read: $!";
my @lines = <$in>;                    # slurp all lines
close $in;

s/e/E/g for @lines;                   # transform each line's $_

open my $out, '>', $path or die "write: $!";
print {$out} @lines;                  # write them back
close $out;
# file now holds:
# rEd
# grEEn
# bluE

המלכודת: open מחזיר שקר בכישלון במקום לזרוק חריגה, לכן הניב or die "...: $!" הוא חובה - $! היא מחרוזת שגיאת המערכת, הכישלון השקט 2>/dev/null של ה־shell שנעשה קולני ומפורש. וכשאתם בונים פקודה ממשתנים, העדיפו את צורת הרשימה של system/open ('ls', '-l', $dir) על פני מחרוזת מאונטרפלת יחידה: צורת הרשימה עוקפת את ה־shell לחלוטין, כך שאין פיצול־מילים, אין הרחבת glob, ואין ציטוט שאפשר לטעות בו - התרופה לגיהינום הציטוט של ה־shell.

שורות־קוד־אחת ומתגים#

שורת־הקוד־האחת היא המגרש הביתי של מפתח ה־shell וגם של Perl: המסנן שאתם משלשלים לשרשרת צינורות. משפחת המתגים שגורמת לזה לעבוד: -e מספק את התוכנית, -n עוטף אותה בלולאה ללא הדפסה אוטומטית, -p עוטף אותה בלולאת הדפסה אוטומטית, -i עורך קבצים במקום, -l מטפל בסיומות שורה, ו־-a/-F מפצלים אוטומטית כל שורה אל @F (המקבילה הישירה של awk, שדות ב־@F במקום $1..$NF). BEGIN { } ו־END { } רצים לפני ואחרי הלולאה, כמו ה־BEGIN/END של awk.

grep error log.txt                            # filter lines
awk -F: '{print $1}' /etc/passwd              # first field
awk '{s += $NF} END {print s}' data.txt       # sum last field
perl -ne 'print if /error/' log.txt           # filter lines
perl -F: -ane 'print "$F[0]\n"' /etc/passwd   # first field, -F sets split
perl -lane '$s += $F[-1]; END { say $s }' data.txt  # sum last field

אלה מוצגים ללא פלט מכיוון שמתגי הלולאה המשתמעת אינם מזוהים על ידי build זה של pperl עדיין (ראו את ההערה בראש). כל אחד ממופה לצורת לולאה מפורשת מאומתת: perl -ne 'CODE' הוא while (<$fh>) { CODE }, ו־perl -pe 'CODE' הוא אותו דבר עם print נגרר. הצורה הניתנת־להרצה ”השדה הראשון של כל שורה“:

use v5.42;
open my $fh, '<', '/tmp/passwd.txt' or die "open: $!";
while (<$fh>) {              # the implicit loop of -ane
    chomp;
    my @F = split /:/;       # what -F: gives you in @F
    say $F[0];               # print the first field
}
close $fh;
# root
# daemon
# bin

המלכודת: עד שהמתגים ייכנסו ל־pperl, כתבו את הלולאה המפורשת (היא רצה היום וזה ממילא מה שהמתגים מתרחבים אליו), ועיינו במדריך שורות־הקוד־האחת לקטלוג המתגים המלא ולעשרות מתכונים - זה הדבר בעל המנוף הגבוה ביותר לקריאה בהגעה מרקע של shell, שבו שורת־הקוד־האחת היא הרפלקס היומיומי.

זרימת בקרה#

ה־for, while, case, ו־if/test של ה־shell ממופים אל לולאות ותנאים של Perl, וכמה מהמיפויים ישירים באופן נעים.

לולאות. for f in *; do הופך ל־for my $f (glob "*"); ה־glob של הקובץ *.txt הוא glob "*.txt" או צורת ה־readline <*.txt>.

for f in /tmp/sh_*.txt; do
    echo "file: $f"
done
use v5.42;
for my $f (sort glob "/tmp/sh_*.txt") {
    say "file: $f";
}
# file: /tmp/sh_data.txt
# file: /tmp/sh_passwd.txt

תנאים ו־test. זה מיפוי משמח: בדיקות הקובץ [ -e ], [ -d ], [ -f ] של ה־shell הן אותם אופרטורי -X ב־Perl. השוואה מחרוזתית ומספרית מתפצלות לשתי משפחות, כפי שהן עושות בין [ "$a" = "$b" ] ל־[ "$a" -eq "$b" ]: Perl משתמשת ב־eq/ne למחרוזות וב־==/!= למספרים. ה־-z/-n של ה־shell (מחרוזת ריקה / לא־ריקה) הופכים ל־!length / length.

if [ -f "$f" ] && [ -n "$name" ]; then
    echo ok
fi
[ "$a" = "$b" ]        # string equality
[ "$x" -eq "$y" ]      # numeric equality
use v5.42;
my $f = '/tmp/sh_data.txt';
my $name = 'set';
say "ok" if -f $f && length $name;   # same -f, same idea
# ok

say "abc" eq "abc" ? "eq" : "ne";    # string compare
say 10 == 10.0 ? "num-eq" : "num-ne"; # numeric compare
# eq
# num-eq

case. ל־case של ה־shell אין תאום מילת־מפתח יחיד; ה־Perl האידיומטית היא שרשרת for/if או מפל טרנארי (או, להסתעפויות רבות, האש שיגור הממפה מפתחות להפניות קוד).

case "$cmd" in
    start)   echo starting ;;
    stop)    echo stopping ;;
    *)       echo "unknown: $cmd" ;;
esac
use v5.42;
for my $cmd (qw(start stop bogus)) {
    my $r =
        $cmd eq 'start' ? "starting"
      : $cmd eq 'stop'  ? "stopping"
      :                   "unknown: $cmd";
    say "$cmd -> $r";
}
# start -> starting
# stop -> stopping
# bogus -> unknown: bogus

סטטוס יציאה. לאחר system(), סטטוס התהליך־הבן נמצא ב־$?, בדיוק משתנה ה־shell. אך $? מחזיק את סטטוס ה־wait הגולמי, לא את קוד היציאה החשוף: הזיזו ימינה ב־8 כדי לקבל את הקוד שה־shell היה מציג כ־$?. מילות־המפתח של הלולאה last ו־next הן ה־break וה־continue של ה־shell.

use v5.42;
system('sh', '-c', 'exit 3');
say "raw \$? = $?";           # raw $? = 768
say "exit code = ", $? >> 8;  # exit code = 3

המלכודת: $? >> 8 הוא קוד היציאה; 8 הסיביות הנמוכות מחזיקות את האות שהרג את התהליך־הבן (אם בכלל). ה־shell מסתיר זאת מאחורי ה־$? שלו עצמו; ב־Perl אתם עושים את ההזזה בעצמכם. שכחתה היא הסיבה לכך ש־if ($? == 1) לאחר פקודה שיצאה עם 1 הוא כמעט תמיד שגוי - הערך הגולמי הוא 256, לא 1.

משתנים ונתונים#

משתנה shell הוא תמיד מחרוזת - var=value, בשימוש כ־$var או ${var}, ומצוטט כ־"$var" כדי לשרוד פיצול־מילים. הסקלר של Perl הוא my $var, מתוחם מילונית, ואין צורך בריקוד ציטוט: סקלר Perl מחזיק ערך אחד ואינו עובר פיצול־מחדש בשימוש. היכן שה־shell מכריח את הכול דרך מחרוזות, Perl מוסיפה את שני המכלים של־shell מעולם לא היו: @arrays מסודרים ו־%hashes של מפתח/ערך.

name="Ada"
greeting="Hello, ${name}"
langs="Perl Python"          # a "list" is really a string + IFS
use v5.42;
my $name = "Ada";
my $greeting = "Hello, $name";   # interpolation, like "${name}"
my @langs = ("Perl", "Python");  # a real list, not a split string
say $greeting;                   # Hello, Ada
say "first: $langs[0]";          # first: Perl
say "count: ", scalar @langs;    # count: 2

ארגומנטי סקריפט. הפרמטרים המיקומיים של ה־shell ממופים ל־@ARGV. שדה־המוקשים של הציטוט של "$@" מול $* פשוט אינו קיים: @ARGV הוא מערך אמיתי, כל ארגומנט איבר נקי לא משנה אילו רווחים או globs הכיל. $1..$9 הופכים ל־$ARGV[0]..$ARGV[8], $# (הספירה) הופך ל־scalar @ARGV, ו־$0 (שם הסקריפט) הופך ל־$0.

echo "count: $#"
echo "first: $1"
echo "all: $@"
use v5.42;
say "count: ", scalar @ARGV;   # like $#
say "first: $ARGV[0]";         # like $1
say "all: @ARGV";              # like "$@"
# invoked as: pperl script.pl one two three
# count: 3
# first: one
# all: one two three

מערכים אסוציאטיביים. האש Perl היא ה־declare -A של ה־shell שנעשה כראוי - מוצהר עם %, ניגשים אליו ערך אחד בכל פעם עם $h{key}, וארגונומי הרבה יותר מהמערכים האסוציאטיביים המוברגים של ה־shell.

use v5.42;
my %count;
$count{$_}++ for qw(apple pear apple fig apple pear);
for my $k (sort keys %count) {
    say "$k: $count{$k}";
}
# apple: 3
# fig: 1
# pear: 2

המלכודת: משתנה shell הוא גלובלי כברירת מחדל ומשתנה my של Perl מתוחם מילונית לבלוק שלו - ברירת המחדל ההפוכה. ה־local של ה־shell (תיחום דינמי בתוך פונקציה) קרוב ביותר ל־local של Perl, אך למשתנים רגילים אתם רוצים my, שהוא תיחום מילוני אמיתי. ואינטרפולציה קורית רק בגרשיים כפולים: "$name" מבצע אינטרפולציה, '$name' הוא ארבעת התווים המילוליים, בדיוק כמו ה־shell.

ל־Perl יש גם תת־שגרות מלאות, הפניות, מודולים, ותכנות מונחה־עצמים אם הסקריפט שלכם גדל מעבר לשורת־קוד־אחת - ראו את מדריך התכנות מונחה־העצמים ואת שאר מדריכי ההגעה־מ.

מלכודות בהגעה מ־shell#

המלכודות, מקובצות לסריקה אחרונה לפני שאתם כותבים:

  • <$fh> שומר על תו השורה החדשה הנגרר. read של ה־shell מקצץ; Perl אינו. chomp הוא הרפלקס שלכם בכל קריאת שורה.

  • split הוא שלב נפרד, והוא מבוסס־0. שדות הם $f[0], $f[1], לא ה־$1 המבוסס־1 של awk. split ' ' (רווח לבן קסום) מקצץ ודוחס; split / / (רווח מילולי) אינו.

  • s/// מחזיר ספירה, לא את המחרוזת החדשה. my $x = $s =~ s/a/b/ שם את ספירת ההחלפות ב־$x. העתיקו תחילה ((my $t = $s) =~ s/.../.../) או השתמשו בדגל /r.

  • $? >> 8 הוא קוד היציאה. ה־$? הגולמי לאחר system הוא סטטוס ה־wait המלא; קוד היציאה הוא 8 הסיביות הגבוהות. if ($? == 1) הוא כמעט תמיד באג; אתם רוצים if (($? >> 8) == 1).

  • open אינו זורק חריגה בכישלון. השתמשו ב־open my $fh, '<', $path or die "...: $!". $! היא מחרוזת שגיאת המערכת - הכישלון השקט של ה־shell שנעשה מפורש.

  • העדיפו את צורת הרשימה של system/open על פני מחרוזת מאונטרפלת. system('ls', '-l', $dir) אינו מריץ shell: אין פיצול־מילים, אין הפתעת glob, אין גיהינום ציטוט. צורת המחרוזת (system "ls -l $dir") מחזירה כל מלכודת ציטוט־shell שבאתם לכאן לברוח ממנה.

  • my הוא מילוני; משתני shell הם גלובליים. הצהירו עם my ותחמו לבלוק; הישענו על local רק עבור התנהגות התיחום הדינמי של ה־shell, שלעיתים רחוקות היא מה שאתם רוצים.

  • השוואה מחרוזתית מול מספרית היא שתי משפחות. eq/ne למחרוזות, ==/!= למספרים - הפיצול [ = ] מול [ -eq ], אך האופרטורים הפוכים מה־shell, לכן קראו אותם בזהירות.

  • מתגי הלולאה המשתמעת (-n/-p/-a/-F/-i/-l) עדיין אינם מזוהים על ידי build זה של pperl. רק -e עובד היום. כתבו את הלולאה המפורשת while (<$fh>) { ... }, שהיא בדיוק מה שהמתגים מתרחבים אליו, עד שהם ייכנסו.