Μονογραμμικά οδηγούμενα από regex#

Το προηγούμενο κεφάλαιο κράτησε τα regexps στο επίπεδο που γνωρίζουν ήδη οι χρήστες sed ή awk: κυριολεκτικά μοτίβα μέσα σε m// και s///. Το παρόν κεφάλαιο καλύπτει μονογραμμικά των οποίων η μορφή εξαρτάται από κάποιο χαρακτηριστικό regex πέρα από την απλή αντιστοίχιση - εξαγωγές, συλλήψεις, διεκδικήσεις γύρω, κώδικα-στην-αντικατάσταση και μη καταστροφικές επανεγγραφές.

Οι αναγνώστες που γνωρίζουν ήδη τις κατασκευές ((?:...), (?<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

Οι ονομαστικές συλλήψεις αξίζουν τον κόπο μόλις το μοτίβο έχει πάνω από δύο ομάδες, ή όταν η ίδια ομάδα χρειάζεται αναφορά τόσο στο μοτίβο όσο και στον κώδικα αντικατάστασης.

Πρόσθια και οπίσθια διεκδίκηση#

Διεκδικήσεις μηδενικού πλάτους: ταιριάζουν με μια θέση όπου το περιβάλλον κείμενο ικανοποιεί μια συνθήκη, χωρίς να καταναλώνουν την ίδια τη συνθήκη.

# 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

Συνηθισμένα ζεύγη:

Διεκδίκηση γύρω

Σημασία

(?=X)

Οι επόμενοι χαρακτήρες ταιριάζουν με X

(?!X)

Οι επόμενοι χαρακτήρες δεν ταιριάζουν με X

(?<=X)

Οι προηγούμενοι χαρακτήρες ταιριάζουν με X

(?<!X)

Οι προηγούμενοι χαρακτήρες δεν ταιριάζουν με X

Η οπίσθια διεκδίκηση στο pperl υποστηρίζει μοτίβα μεταβλητού μήκους· δείτε κανονικές εκφράσεις: άγκυρες και διεκδικήσεις.

Πολυγραμμικές αντιστοιχίσεις σε ρουφηγμένη είσοδο#

Το -0777 ρουφάει ολόκληρο το αρχείο στο $_. Οι τροποποιητές 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 από αρχείο καταγραφής αιτημάτων#

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// για προμεταγλώττιση μοτίβων που αναφέρονται με όνομα μέσα σε μονόγραμμο.