חד־שורתיים מבוססי regex#

הפרק הקודם שמר את ה־regexps ברמה שמשתמשי sed או awk כבר מכירים: תבניות ליטרליות בתוך m// ו־s///. פרק זה מכסה חד־שורתיים שצורתם תלויה בתכונת regex מעבר להתאמה פשוטה - חילוצים, לכידות, lookarounds, קוד־בהחלפה, ושכתובים לא־הרסניים.

קוראים המכירים כבר את המבנים ((?:...), (?<name>...), (?=...), המתאמים /e ו־/r) יכולים לדפדף לקבלת המסגרת של שורת הפקודה. קוראים היודעים מה /g עושה אך מעולם לא כתבו /e יפיקו את המרב מקריאה רצופה.

לשפת ה־regex עצמה, ראו את מדריך הביטויים הרגולריים.

חילוץ כל ההתאמות#

/g בהקשר רשימה מחזיר כל התאמה, לא רק את הראשונה.

# Every integer in the input
pperl -ne 'print "$_\n" for /-?\d+/g' file.txt

# Every IPv4-shaped substring
pperl -lne 'print for /\b(?:\d{1,3}\.){3}\d{1,3}\b/g' access.log

# Every HTTP header value (naive: no folded headers)
pperl -nE 'say $1 if /^[\w-]+:\s*(.+)$/' headers.txt

say (מופעל על־ידי -E) מוסיף שורה חדשה, חוסך "\n" אחד ושומר על תוכנית קצרה.

מכמתים לא־חמדניים#

*?, +?, ??, {n,m}? - להתאים כמה שפחות תווים.

# Everything between the first < and the next > (tag-by-tag)
pperl -lne 'print for /<(.+?)>/g' markup.html

# Every quoted string (double quotes, no escape handling)
pperl -lne 'print for /"([^"]*)"/g' config.txt

מחלקת התווים השלילית [^"]* היא בדרך כלל לא־חמדנית מהירה ובטוחה יותר מאשר .*? כשאתם יודעים מה אסור שיופיע בפנים.

לכידות בהחלפה#

הצד הימני של s/// יכול להפנות ללכידות בצד השמאלי.

# Swap every "word1 word2" pair
pperl -pe 's/(\w+)\s+(\w+)/$2 $1/g' file.txt

# Quote every unquoted key in key=value pairs
pperl -pe 's/(\w+)=/$1="/g; s/=([^"\n]+)/"$1"/g' config.txt

# Increase every trailing number by one
pperl -pe 's/(\d+)$/$1 + 1/e' file.txt

/e - ההחלפה היא קוד Perl#

המתאם /e מתייחס להחלפה כקוד; ערך החזרה שלו הוא מה שמוחלף.

# Number every word in the file (global counter)
pperl -pe 's/(\w+)/++$i . ".$1"/ge' file.txt

# Number words per line (counter reset each line)
pperl -pe '$i = 0; s/(\w+)/++$i . ".$1"/ge' file.txt

# Wrap every integer in base 16
pperl -pe 's/\d+/sprintf "0x%x", $&/ge' file.txt

# Convert Celsius-labelled numbers to Fahrenheit
pperl -pe 's/(\d+)C\b/sprintf "%.1fF", $1 * 9 / 5 + 32/ge' weather.txt

/ge ביחד: להעריך את הצד הימני כקוד, לעשות זאת לכל התאמה. $& הוא כל הטקסט שהותאם; $1, $2, … הן לכידות.

/ee מעריך פעמיים - ה־e הראשון מייצר מחרוזת, השני מעריך את אותה מחרוזת. שימושי כשמחרוזת ההחלפה היא בעצמה קוד דינמי שנקרא מקלט; נדרש לעיתים נדירות, סכנה לעיתים קרובות.

/r - החלפה לא־הרסנית#

/r מחזיר את המחרוזת המשונה במקום לשכתב את $_. המקור נשאר ללא שינוי.

# Print filename and its .bak variant side by side
ls *.txt | pperl -pe '$_ = $_ . ($_ =~ s/$/.bak/r)'

# Compute a transformed version without touching $_
pperl -ne 'my $upper = $_ =~ tr/a-z/A-Z/r; print $_, $upper' file.txt

פנו ל־/r כששורה יחידה צריכה להיות מודפסת בשתי צורות, או כשהטרנספורמציה מותנית ואתם רוצים את השורה ללא שינוי חזרה בענף הכישלון.

לכידות בעלות שם#

(?<name>...) לוכד לתוך $+{name} ולתוך %+.

# Parse combined-log-format access lines
pperl -nE '
    if (/^(?<ip>\S+).*?"(?<method>\S+) (?<path>\S+)/) {
        say "$+{ip} $+{method} $+{path}";
    }
' access.log

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

lookahead ו־lookbehind#

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

# Digits preceded by $ (e.g. "$42" → 42, ignore "42")
pperl -lne 'print for /(?<=\$)\d+/g' prices.txt

# Words NOT followed by "!"
pperl -lne 'print for /\b\w+\b(?!!)/g' shouts.txt

# Insert CRLF only where LF is not already preceded by CR
pperl -pe 's/(?<!\r)\n/\r\n/g' unix.txt

צמדים נפוצים:

Lookaround

משמעות

(?=X)

התווים הבאים מתאימים ל־X

(?!X)

התווים הבאים אינם מתאימים ל־X

(?<=X)

התווים הקודמים מתאימים ל־X

(?<!X)

התווים הקודמים אינם מתאימים ל־X

lookbehind ב־pperl תומך בתבניות באורך משתנה; ראו ביטויים רגולריים: עוגנים והיגדים.

התאמות רב־שורתיות על פני קלט slurped#

-0777 עושה slurp לכל הקובץ לתוך $_. המתאמי regex שחשובים במצב זה:

  • /s - . מתאים ל־\n.

  • /m - ^ ו־$ מתאימים בכל התחלה/סוף שורה, לא רק בהתחלה/סוף של כל המחרוזת.

# Every BEGIN...END block (non-greedy), each on one output line
pperl -0777 -lne '
    while (/BEGIN(.*?)END/sg) {
        (my $body = $1) =~ s/\n/ /g;
        print $body;
    }
' text.txt

# Paragraphs where every line starts with ">"
pperl -00 -ne 'print if /\A(?:>.*\n?)+\z/m' mail.txt

\A ו־\z מעגנים את התחלת וסוף כל המחרוזת, בלתי־מושפעים מ־/m. השתמשו בהם כש־/m דלוק אבל אתם באמת מתכוונים לכל המחרוזת.

תעתוק עם tr#

tr (נכתב גם y) היא החלפה תו־אחר־תו. לא regex, אבל שכן קרוב - והכלי הנכון לעבודות עם מערכי תווים.

pperl -pe 'tr/A-Za-z/N-ZA-Mn-za-m/'               # ROT13
pperl -pe 'tr/A-Za-z/a-zA-Z/'                     # swap case
pperl -pe 'tr/a-z//d'                             # delete lowercase letters
pperl -pe 'tr/ \t/ /s'                            # squeeze whitespace runs to one space

# Count commas on each line (tr returns the count)
pperl -ne 'print tr/,//, "\n"' data.csv

הדגל d מוחק תווים בקבוצה השמאלית שאין להם מקבילה בימנית. הדגל s מרסק רצפים. הדגל c משלים את הקבוצה השמאלית (כל מה שלא מופיע ברשימה).

ניתוח תת־מחרוזות מופרדות#

CSV עם פסיקים מצוטטים#

פיצול מהיר־ומלוכלך המכבד שדות במרכאות כפולות:

pperl -ne '
    my @F = /"([^"]*)"|([^,]+)/g;
    @F = grep defined, @F;
    print join("|", @F), "\n";
' quoted.csv

נכון ומהיר מספיק לחד־פעמי. לכל דבר שחייב לטפל במרכאות מוטמעות ("he said ""hi"""), השתמשו ב־Text::CSV; ראו numeric.

מחרוזות שאילתת URL#

# Each key=value pair on its own line
pperl -lne 'print for /([^?&=]+)=([^&]*)/g' urls.txt

חד־שורתיי חילוץ מעשיים#

מילים לפי אורך#

# Words of exactly 5 characters
pperl -lne 'print for /\b\w{5}\b/g' file.txt

מספרים עם יחידות#

pperl -lne 'print for /\b\d+(?:\.\d+)?\s*(?:ms|s|kb|mb|gb)\b/gi' timings.txt

HTTP User-Agent מ־log בקשות#

pperl -nE 'say $1 if /^User-Agent: (.+)$/' request.log

סוגריים מסולסלים מאוזנים (עד כמה שמנוע ה־regex של pperl תומך)#

מנוע ה־regex של pperl תומך ברקורסיה דרך (?R) ו־(?1), ולכן איזון סוגריים ברמה יחידה הוא חד־שורתי:

pperl -0777 -nE 'say $& while /\{(?:[^{}]|(?R))*\}/g' code.txt

למבנים מקוננים עמוקות, פנו לפרסר. Regex הוא הכלי הנכון עד למורכבות שבה הוא מפסיק להיות כזה.

לקריאה נוספת#

  • מדריך הביטויים הרגולריים - השפה תחת כל מתכון כאן, כולל מתאמים, ביצועים, ו־Unicode.

  • m - אופרטור ההתאמה.

  • s - החלפה, המערך המלא של המתאמים.

  • tr - מדריך עיון לתעתוק.

  • progression - מתכוני s/// פשוטים יותר ללא לכידות או קוד.

  • perlop - הצורה qr// להידור מוקדם של תבניות המופנות בשם בתוך חד־שורתי.