Οι κανονικές εκφράσεις (regex) είναι ακολουθίες χαρακτήρων που ορίζουν ένα μοτίβο αναζήτησης μέσα σε κείμενο.
Η εκμάθηση της σύνταξης των κανονικών εκφράσεων ξεπερνά τα όρια αυτού του θέματος.
Θα επικεντρωθούμε στις εκφράσεις που παρέχει το jq για να αξιοποιείς τις κανονικές εκφράσεις.
Διαφορετικά εργαλεία υλοποιούν διαφορετικές εκδόσεις των κανονικών εκφράσεων.
Το jq ενσωματώνει τη βιβλιοθήκη κανονικών εκφράσεων Oniguruma, η οποία είναι σε μεγάλο βαθμό συμβατή με τις κανονικές εκφράσεις της Perl v5.8.
Η συγκεκριμένη σύνταξη που χρησιμοποιεί το jq βρίσκεται στο αποθετήριο GitHub του Oniguruma.
Το jq δεν έχει κάποια ειδική σύνταξη για κανονικές εκφράσεις.
Απλώς εκφράζονται ως συμβολοσειρές.
Αυτό σημαίνει ότι οι ανάστροφες κάθετοι μέσα στην κανονική έκφραση πρέπει να διαφύγουν μέσα στη συμβολοσειρά.
Για παράδειγμα, η κλάση χαρακτήρων για ψηφία (\d) πρέπει να γραφτεί ως "\\d".
Οι κανονικές εκφράσεις στο jq περιορίζονται σε ένα σύνολο φίλτρων.
Όταν θέλεις να μάθεις αν μια συμβολοσειρά ταιριάζει με ένα μοτίβο, χρησιμοποίησε το φίλτρο test.
STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])
Αυτό το φίλτρο βγάζει ένα αποτέλεσμα Boolean.
"Hello World!" | test("W") # => true
"Goodbye Mars" | test("W") # => false
Όταν θέλεις να εξαγάγεις την υποσυμβολοσειρά που όντως ταίριαξε με το μοτίβο, χρησιμοποίησε το φίλτρο match.
STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])
Αυτό το φίλτρο βγάζει:
Αυτό το παράδειγμα ψάχνει δύο ίδια συνεχόμενα φωνήεντα, χρησιμοποιώντας τη σύνταξη backreference, \1.
"Hello World!" | match("([aeiou])\\1")
# => empty
"Goodbye Mars" | match("([aeiou])\\1")
# => {
# "offset": 1,
# "length": 2,
# "string": "oo",
# "captures": [
# {
# "offset": 1,
# "length": 1,
# "string": "o",
# "name": null
# }
# ]
# }
Το φίλτρο match επιστρέφει ένα αντικείμενο για κάθε αντιστοίχιση.
Αυτό το παράδειγμα δείχνει τη σημαία "g" σε δράση, για να βρει όλα τα φωνήεντα.
"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
# { "offset": 2, "length": 1, "string": "o", "captures": [] }
# { "offset": 6, "length": 1, "string": "e", "captures": [] }
# { "offset": 9, "length": 1, "string": "a", "captures": [] }
Παρόμοια με το φίλτρο match, το φίλτρο capture επιστρέφει ένα αντικείμενο αν υπήρξε αντιστοίχιση.
STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])
Το αντικείμενο που επιστρέφεται είναι μια αντιστοίχιση των ονομασμένων συλλήψεων.
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
# "project": "JIRAISSUE",
# "issue_num": "1234"
# }
Το φίλτρο scan είναι παρόμοιο με το match με τη σημαία "g".
STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)
# note, there is no scan([REGEX, FLAGS]) version, unlike other filters
Το scan θα βγάλει μια ροή υποσυμβολοσειρών.
"Goodbye Mars" | scan("[aeiou]")
# => "o"
# "o"
# "e"
# "a"
Χρησιμοποίησε τον κατασκευαστή πίνακα [...] για να συλλάβεις τις υποσυμβολοσειρές.
"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
Αν ξέρεις ποια μέρη της συμβολοσειράς θέλεις να κρατήσεις, χρησιμοποίησε το match ή το scan.
Αν ξέρεις ποια μέρη θέλεις να πετάξεις, χρησιμοποίησε το split.
STRING | split(REGEX; FLAGS)
Το φίλτρο split με ένα όρισμα αντιμετωπίζει το όρισμά του ως σταθερή συμβολοσειρά.
Για να χρησιμοποιήσεις κανονική έκφραση με το split, πρέπει να δώσεις το δεύτερο όρισμα· δεν πειράζει να χρησιμοποιήσεις μια κενή συμβολοσειρά.
Ένα παράδειγμα που χωρίζει μια συμβολοσειρά σε αυθαίρετα κενά.
"first second third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
Αυτό συμβαίνει αν ξεχάσουμε το όρισμα με τις σημαίες.
"first second third fourth" | split("\\s+")
# => ["first second third fourth"]
Μόνο ένα αποτέλεσμα: η σταθερή συμβολοσειρά \s+ δεν βρέθηκε στην είσοδο.
Το split με ένα όρισμα δεν μπορεί να χειριστεί αυθαίρετα κενά.
Ο διαχωρισμός με βάση το κενό δίνει αυτό το αποτέλεσμα.
"first second third fourth" | split(" ")
# => ["first", "", "", "second", "", "", "", "",
# "", "", "", "", "", "", "third", "fourth" ]
Τα φίλτρα sub και gsub μπορούν να μετασχηματίσουν τη συμβολοσειρά εισόδου, αντικαθιστώντας τα τμήματα της εισόδου που ταιριάζουν με μια συμβολοσειρά αντικατάστασης.
Για να αντικαταστήσεις μόνο την πρώτη αντιστοίχιση, χρησιμοποίησε το sub.
Για να αντικαταστήσεις όλες τις αντιστοιχίσεις, χρησιμοποίησε το gsub.
STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."
"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."
Το κείμενο αντικατάστασης μπορεί να αναφέρεται στις υποσυμβολοσειρές που ταίριαξαν· χρησιμοποίησε ονομασμένες συλλήψεις και παρεμβολή συμβολοσειρών.
"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b"; # find words 3 letters long
"\(.tla | ascii_upcase)" ) # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"
Σε όλα τα παραπάνω φίλτρα, το FLAGS είναι μια συμβολοσειρά που αποτελείται από μηδέν ή περισσότερες από τις υποστηριζόμενες σημαίες.
g - Καθολική αναζήτηση (βρίσκει όλες τις αντιστοιχίσεις, όχι μόνο την πρώτη)i - Αναζήτηση χωρίς διάκριση πεζών-κεφαλαίωνm - Λειτουργία πολλαπλών γραμμών (το '.' θα ταιριάζει και με αλλαγές γραμμής)n - Αγνόηση κενών αντιστοιχίσεωνp - Ενεργοποιούνται και οι δύο λειτουργίες, s και ms - Λειτουργία μονής γραμμής ('^' -> '\A', '$' -> '\Z')l - Βρίσκει τις μεγαλύτερες δυνατές αντιστοιχίσειςx - Εκτεταμένη μορφή κανονικής έκφρασης (αγνοεί τα κενά και τα σχόλια)Για παράδειγμα
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# or with Extended formatting
"JIRAISSUE-1234" | capture("
(?<project> \\w+ ) # the Jira project
- # followed by a hyphen
(?<issue_num> \\d+ ) # followed by digits
"; "x")