מתכונים מתקדמים#
פרק זה עובד מהחלפות טריוויאליות, דרך רדוקציות, חילוץ ופלט מובנה. כל סעיף משתמש מחדש ב־switches שהוצגו בקודמו, כך שקורא שעובר ברצף בונה את אוצר המילים של הניבים מבלי לפגוש דגל חדש באמצע מתכון.
קוראים המגיעים מ־awk/sed אמורים לזהות את הסעיפים הראשונים; החומר המעניין מתחיל בסביבות Reductions across lines ו־Context-windowed filters.
כל מתכון ניתן להעתקה והדבקה. כאשר קלט לדוגמה חשוב, הוא מוצג inline כך שהפלט הצפוי חד־משמעי.
הבסיס: -e ו־-E#
החד־שורתי הקצר ביותר. ללא לולאת I/O, ללא פיצול שדות - רק תוכנית שרצה פעם אחת.
pperl -e 'print 2 ** 32, "\n"' # 4294967296
pperl -E 'say 2 ** 32' # same, with -E/say
pperl -E 'say for 1 .. 5' # 1\n2\n3\n4\n5
ה־, ב־print מפריד בין ארגומנטים; מפריד שדה הפלט $, (ברירת מחדל ריק) נכנס ביניהם.
סינון: -n + print if#
-n עוטף את התוכנית בלולאת קריאה על כל שורה של כל קובץ קלט (או STDIN). דבר אינו מודפס אלא אם התוכנית אומרת זאת.
# Sample input
$ printf 'gate\napple\nwhat\nkite\n' > words.txt
pperl -ne 'print if /at/' words.txt # gate, what
pperl -ne 'print unless /e/' words.txt # what
pperl -ne 'print if /^[aeiou]/' words.txt # apple
התאמת ה־regex היא מול $_ כברירת מחדל; /at/ הוא קיצור ל־$_ =~ m/at/.
תנאים מרובים#
# Lines containing both "ERROR" and "timeout"
pperl -ne 'print if /ERROR/ && /timeout/' app.log
# Lines containing neither
pperl -ne 'print if !/ERROR/ && !/timeout/' app.log
# Events A, B, C appearing in order on one line
pperl -ne 'print if /A.*B.*C/' events.log
לפי מספר שורה#
pperl -ne 'print if $. == 1' # head -n 1
pperl -ne 'print if $. <= 10' # head -n 10
pperl -ne 'print if 17 .. 30' # lines 17-30 (range op)
pperl -ne 'print if /START/ .. /END/' # regex range, inclusive
pperl -ne 'print unless $. % 2' # even lines
אופרטור הטווח .. בהקשר סקלר הוא flip-flop: מושווה מול $. כששתי נקודות הקצה הן מספרים שלמים, מול $_ כשהן תבניות. ראו perlop.
לפי אורך#
השתמשו ב־-l כך שהאורך הנספר אינו כולל את תו השורה החדשה הנגרר.
pperl -lne 'print if length >= 80' src.c # long lines
pperl -lne 'print if length() < 5' words.txt # short lines
length חשוף ללא ארגומנט חל על $_.
החלפה: -p + s///#
-p הוא -n בתוספת print מובלע לאחר כל איטרציה. השתמשו בו כשהפלט הוא הקלט (אולי לאחר שינוי).
# Replace every "foo" with "bar"
pperl -pe 's/foo/bar/g' input.txt
# First occurrence per line only
pperl -pe 's/foo/bar/' input.txt
# Conditional: replace only on lines that also match BAZ
pperl -pe 's/foo/bar/g if /BAZ/' input.txt
טרנספורמציות רישיות#
pperl -nle 'print uc' # to uppercase
pperl -nle 'print lc' # to lowercase
pperl -nle 'print ucfirst lc' # Sentence case
pperl -ple 's/(\w+)/\u$1/g' # Title Case Per Word
נרמול רווחים#
pperl -ple 's/^\s+//' # trim left
pperl -ple 's/\s+$//' # trim right
pperl -ple 's/^\s+|\s+$//g' # trim both
pperl -ple 's/\s+/ /g' # collapse runs
pperl -ne 'print if /\S/' # drop blank lines
המרת סיומות שורה#
pperl -pe 's/\r\n/\n/g' win.txt # DOS → UNIX
pperl -pe 's/(?<!\r)\n/\r\n/g' unix.txt # UNIX → DOS
ה־lookbehind (?<!\r) נמנע מייצור \r\r\n כשהקלט כבר מקודד DOS. ראו regex-recipes ל־lookarounds לעומק.
עבודה מבוססת שדות: -a ו־-F#
-a מפצל אוטומטית כל שורה ל־@F. -F משנה את המתחם.
$ cat table.txt
brown bread mat hair 42
blue cake mug shirt -7
yellow banana window shoes 3.14
# Second field
pperl -lane 'print $F[1]' table.txt
# bread, cake, banana
# Last field
pperl -lane 'print $F[-1]' table.txt
# 42, -7, 3.14
# Count fields per line
pperl -lane 'print scalar @F' table.txt
# 5, 5, 5
# Lines whose last field is negative
pperl -lane 'print if $F[-1] < 0' table.txt
# blue cake mug shirt -7
מתחם מותאם אישית#
# /etc/passwd: print username (field 1) and shell (last field)
pperl -F: -lane 'print "$F[0] $F[-1]"' /etc/passwd
# Tab-separated
pperl -F'\t' -lane 'print $F[2]' metrics.tsv
# Comma or semicolon, one or more
pperl -F'[,;]+' -lane 'print scalar @F' mixed.csv
CSV ראוי (שדות מצוטטים עם פסיקים מוטמעים) אינו עבודה ל־-F. השתמשו ב־-MText::CSV - ראו numeric לדפוס.
איחוד מחדש של שדות#
שיבוץ של @F בתוך מרכאות כפולות משתמש ב־$" (ברירת מחדל: רווח אחד) כמפריד.
# Reverse field order, space-separated
pperl -lane 'print "@{[reverse @F]}"' table.txt
# Reverse field order, colon-separated
pperl -F: -lane 'local $" = ":"; print "@{[reverse @F]}"' /etc/passwd
@{[ EXPR ]} הוא ניב הפניה־למערך־ואז־deref המאפשר לבטויים שרירותיים להיות משובצים לתוך מחרוזת במרכאות כפולות.
רדוקציות על פני שורות#
המעבר מסינונים לכל שורה לחישוב חוצה־שורות.
ספירות#
# wc -l
pperl -lne 'END { print $. }' big.txt
# grep -c pattern
pperl -lne '$n++ if /pattern/; END { print $n + 0 }' file
# Count blank lines
pperl -lne '$n++ if /^$/; END { print $n + 0 }' file
# Total fields across the file
pperl -lane '$t += @F; END { print $t }' table.txt
הניב $n + 0 מדפיס 0 במקום ריק כשהמונה מעולם לא הוגדל.
סכומים, מינימה, מקסימה#
השתמשו ב־List::Util דרך -M.
# Per line
pperl -MList::Util=sum -lane 'print sum @F' data.txt
pperl -MList::Util=min -lane 'print min @F' data.txt
pperl -MList::Util=max -lane 'print max @F' data.txt
# Across all lines
pperl -MList::Util=sum -lane '$s += sum @F; END { print $s }' data.txt
pperl -MList::Util=max -alne '$m = max($m // (), @F); END { print $m }' data.txt
$m // () מחזיר רשימה ריקה כש־$m לא־מוגדר, כך שהאיטרציה הראשונה אינה מעבירה undef ל־max. ראו numeric לפרק החשבון המלא.
השורה הארוכה והקצרה ביותר#
pperl -ne '$l = $_ if length > length $l; END { print $l }' file
pperl -ne '$s = $_ if $. == 1 || length < length $s; END { print $s }' file
שורות ייחודיות (הופעה ראשונה בלבד)#
pperl -ne 'print unless $seen{$_}++' file
# Or the List::Util one-liner (slurps):
pperl -MList::Util=uniq -e 'print uniq <>' file
הצורה הראשונה זורמת - הזיכרון גדל עם מספר השורות הייחודיות, לא עם מספר השורות הכולל. השנייה עושה slurp להכל בזיכרון, ואז פולטת. העדיפו את הראשונה לקלטים גדולים.
כפילויות בלבד#
# Print each duplicate line once, when first seen for the second time
pperl -ne 'print if ++$seen{$_} == 2' file
סינונים בחלון הקשר#
הדפסת השורה סביב התאמה, לא רק ההתאמה עצמה.
# Line immediately before each match (grep -B 1, with caveats)
pperl -ne '/pattern/ && $prev && print $prev; $prev = $_' file
# Line immediately after each match (grep -A 1)
pperl -ne 'print if $p; $p = /pattern/' file
# tail -n 10
pperl -ne 'push @a, $_; shift @a if @a > 10; END { print @a }' file
להתנהגות עשירה של -A / -B / -C, grep הוא הכלי הנכון. חד־שורתיים מתאימים כשכלל החלון יוצא דופן (לדוגמה ״הדפס את השורה שמתאימה ל־X, ועוד כל שורה עד השורה הריקה הבאה״).
# Print from /SECTION/ to the next blank line
pperl -ne 'print if /SECTION/ .. /^$/' file
מצב רשומה: -0, -00, -0777#
החליפו לקריאה לא־מבוססת־שורות כשהרשומה הטבעית אינה שורה.
Slurp עם -0777#
# Single-shot regex across the whole file
pperl -0777 -ne 'print "match\n" if /BEGIN.*?END/s' text.txt
# Replace only the first occurrence across the file
pperl -0777 -i.bak -pe 's/TODO/DONE/' notes.txt
-0777 קובע $/ = undef. כל הקובץ נכנס כ־$_ אחד. המתאם /s גורם ל־. להתאים לשורות חדשות. ראו regex-recipes לסיפור הרב־שורתי המלא.
מצב פסקה עם -00#
-00 קובע $/ = "". כל קריאה מחזירה הכל עד מתחם השורה הריקה הבא.
# Paragraphs containing "TODO"
pperl -00 -ne 'print if /TODO/' notes.md
# Reverse paragraph order
pperl -00 -e 'print reverse <>' notes.md
רשומות מופרדות־NUL עם -0#
# Count files produced by find -print0
find . -type f -print0 | pperl -0 -ne '$n++; END { print "$n\n" }'
# Convert NUL-delimited stream to newline-delimited
find . -type f -print0 | pperl -0 -pe 's/\0/\n/'
עריכה במקום עם -i#
-i דורס כל קובץ קלט במה ש־-p מדפיס. -i.bak שומר גיבוי.
# Lowercase every occurrence of HOSTNAME in every .conf (keep backup)
pperl -i.bak -pe 's/HOSTNAME/\L$&/g' *.conf
# Delete every line matching DEPRECATED (no backup - only do this
# after you've validated the pattern on one file)
pperl -i -ne 'print unless /DEPRECATED/' file.txt
קבצי הגיבוי מצטברים. נקו אותם עם rm *.bak לאחר וידוא. ראו gotchas למלכודות הקלאסיות של עריכה במקום.
פלט מובנה#
דמוי־CSV (קל־משקל - פסיקים אינם מצוטטים)#
# Rebuild a TSV as CSV (naive, assumes no commas in fields)
pperl -F'\t' -lane 'print join ",", @F' data.tsv
# Add a column of line numbers
pperl -lane 'print join "\t", $., @F' data.tsv
JSON (stdlib)#
# Emit one JSON object per line (no commas in values assumed)
pperl -MJSON::PP -F: -lane '
print JSON::PP->new->encode({
user => $F[0], uid => 0+$F[2], shell => $F[-1]
})
' /etc/passwd
חד־שורתיים רב־שורתיים כאלה הם המקום בו alias מתחיל להשתלם.
קלט ממוספר#
pperl -pe '$_ = "$. $_"' # 1 gate, 2 apple, ...
pperl -pe 's/^/sprintf "%5d ", $./e' # padded width
המתאם /e על s/// מתייחס להחלפה כקוד Perl. ראו regex-recipes.
לקריאה נוספת#
regex-recipes - חד־שורתיים תלויי־regex: לכידות, lookaround, קבוצות בעלות שם,
/e,/r.numeric - חשבון, סטטיסטיקה, אקראיים, חישובי תאריכים.
aliases - הפכו את המתכונים שאתם משתמשים בהם שבועית לפונקציות shell.
gotchas - מרכאות, קידוד, גיבויי
-i, הפתעות של מפריד רשומה.perlop- אופרטור ה־flip-flop, אופרטור היהלום,s///,tr///, צורות מרכאות.