# מ־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: ```perl use v5.42; ``` שורה אחת זו מפעילה את `strict`, את `warnings`, ואת הפונקציה המובנית `say` (כלומר `print` עם תו שורה חדשה נגרר), מערך התכונות המודרני המשמש לאורך כל הדרך. #### NOTE מתגי שורה־אחת ב־build הזה של pperl התאום הטבעי ב־Perl של מפתח ה־shell הוא שורת־הקוד־האחת: `perl -ne` ו־`perl -pe`, המסנן שבתוך־השרשרת. מתגים אלה הם **perl5 5.42 תקני** ומוצגים לאורך מדריך זה כצורה האידיומטית. אך משפחת מתגי הלולאה המשתמעת (`-n`, `-p`, `-a`, `-F`, `-i`, `-l`) **עדיין אינה מזוהה על ידי build זה של `pperl`** - רק `-e` מזוהה. לכן דוגמאות שורת־הקוד־האחת שלהלן מוצגות *ללא* פלט, וכל אחת מהן מצומדת לצורת סקריפט־מלא עם לולאה מפורשת (`while (<$fh>) { ... }`) ש**אכן** רצה על `pperl` היום ונושאת את הפלט המאומת. השתמשו בצורת הסקריפט המלא עד שהמתגים ייכנסו. ## כיצד לקרוא את זה - [לולאת הרשומה]() - `while read line` הופך ל־`while (<$fh>)` ו־`$_`. - [שדות ופיצול]() - שדות `cut`/`IFS`/`awk` הופכים ל־`split` ולמערך אמיתי. - [ביטויים רגולריים והחלפה]() - הרחבות הפרמטרים `${var#...}`/`${var/a/b}` של ה־shell הופכות לביטויים רגולריים אמיתיים. - [כתובות וטווחים]() - `grep -n`, `awk NR==2`, וטווחי שורות. - [קלט/פלט, צינורות, ועריכה במקום]() - הלב: `open`, `-|`/`|-`, הפניה־מחדש, here-docs, `$(...)`. - [שורות־קוד־אחת ומתגים]() - `-n`, `-p`, `-e`, `BEGIN`/`END`. - [זרימת בקרה]() - `for`/`while`/`case`/`if`, בדיקות קובץ, סטטוס יציאה. - [משתנים ונתונים]() - `$var` הופך ל־`my $var`, בתוספת ה־`@arrays` וה־`%hashes` של־shell מעולם לא היו. - [מלכודות בהגעה מ־shell]() - סרקו לפני שאתם כותבים. ## לולאת הרשומה הניב שורה־בכל־פעם של ה־shell הוא `while read line; do ... done`, הקורא שורה אחת בכל איטרציה אל `$line` (ומקצץ אותה בהתאם ל־`IFS`). המקבילה של Perl היא `while (<$fh>)`, הקוראת שורה אחת בכל איטרציה אל `$_`, משתנה הנושא המשתמע. ההבדל המכריע: Perl שומרת על תו השורה החדשה הנגרר, כך שאתם `chomp` אותו בעצמכם, בדיוק היכן שה־`read` של ה־shell היה אוכל אותו. ```bash while read line; do echo "got: $line" done < data.txt ``` ```perl 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>` החשוף והשורה נוחתת ב־`$_`, ברירת המחדל שעליה פועלות רוב הפונקציות המובנות לשורות. מספר השורה נמצא ב־`$.`. זו הצורה שכדאי להישען עליה כשאתם מתרגמים מסנן: ```perl 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`. ```bash line='root:x:0:0:root:/root:/bin/bash' user=$(echo "$line" | cut -d: -f1) shell=$(echo "$line" | cut -d: -f7) echo "$user $shell" ``` ```perl 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`: ```perl 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///`, המקבל ביטוי רגולרי ממשי ואת מערך הדגלים המלא. ```bash 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 ``` ```perl 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` משנים את ההתאמה. ```perl 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/` כנגד `$_`. ```bash grep -n an data.txt # number + matching lines awk 'NR==2' data.txt # the second line ``` ```perl 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/`, אמת מהשורה התואמת את התבנית השמאלית עד השורה התואמת את הימנית, כולל: ```perl 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`, `<; close $ph; chomp @nums; say "got: @nums"; # got: 1 2 3 ``` ```perl 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`), שמרו את ההפניה־מחדש בתוך הפקודה שאתם מוסרים לגרשים האחוריים: ```perl 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.** ה־`<; # 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`. ```bash 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 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` נגרר. הצורה הניתנת־להרצה ”השדה הראשון של כל שורה“: ```perl 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`, כתבו את הלולאה המפורשת (היא רצה היום וזה ממילא מה שהמתגים מתרחבים אליו), ועיינו ב[מדריך שורות־הקוד־האחת](../oneliner/index.md) לקטלוג המתגים המלא ולעשרות מתכונים - זה הדבר בעל המנוף הגבוה ביותר לקריאה בהגעה מרקע של shell, שבו שורת־הקוד־האחת היא הרפלקס היומיומי. ## זרימת בקרה ה־`for`, `while`, `case`, ו־`if`/`test` של ה־shell ממופים אל לולאות ותנאים של Perl, וכמה מהמיפויים ישירים באופן נעים. **לולאות.** `for f in *; do` הופך ל־`for my $f (glob "*")`; ה־glob של הקובץ `*.txt` הוא `glob "*.txt"` או צורת ה־readline `<*.txt>`. ```bash for f in /tmp/sh_*.txt; do echo "file: $f" done ``` ```perl 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`. ```bash if [ -f "$f" ] && [ -n "$name" ]; then echo ok fi [ "$a" = "$b" ] # string equality [ "$x" -eq "$y" ] # numeric equality ``` ```perl 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` או מפל טרנארי (או, להסתעפויות רבות, האש שיגור הממפה מפתחות להפניות קוד). ```bash case "$cmd" in start) echo starting ;; stop) echo stopping ;; *) echo "unknown: $cmd" ;; esac ``` ```perl 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. ```perl 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` של מפתח/ערך. ```bash name="Ada" greeting="Hello, ${name}" langs="Perl Python" # a "list" is really a string + IFS ``` ```perl 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`. ```bash echo "count: $#" echo "first: $1" echo "all: $@" ``` ```perl 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. ```perl 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 יש גם תת־שגרות מלאות, הפניות, מודולים, ותכנות מונחה־עצמים אם הסקריפט שלכם גדל מעבר לשורת־קוד־אחת - ראו את [מדריך התכנות מונחה־העצמים](../oop/index.md) ואת שאר מדריכי ההגעה־מ. ## מלכודות בהגעה מ־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>) { ... }`, שהיא בדיוק מה שהמתגים מתרחבים אליו, עד שהם ייכנסו.