Bash: Επεξεργασία κειμένου με Regular Expressions και άλλα κόλπα

dimitris | Πέμ, 08/22/2013 - 16:20 | 20' | 1

Μάθετε τα μυστικά του κελύφους του Linux και κερδίστε χρόνο για την επεξεργασία κειμένου από την γραμμή εντολών με regular expressions. Γνωρίστε το shebang, και το here και παίξτε με τις εντολές awk και sed, αλλά και τις head, tail, cut, grep, και curl

Το μεγαλύτερο μέρος του tutorial αυτού, ασχολείται με τους μεταχαρακτήρες, αυτα τα περίεργα "*?$^!" σύμβολα που εμφανίζονται διάσπαρτα σε εντολές.

Εάν κατανοήσετε την λειτουργία τους και μάθετε πώς να τα χρησιμοποιείτε, θα είστε σε θέση να δημιουργείτε πανίσχυρα προγράμματα για την εύρεση, εισαγωγή και τον "καθαρισμό" ενός plaintext κειμένου.

Το πρώτο μας παράδειγμα θα σας βοηθήσει στο πως μπορείτε να χρησιμοποιήσετε ένα πρόγραμμα για να αναγνωρίζετε ένα συγκεκριμένο κομμάτι κειμένου και να το επεξεργάζεται μετέπειτα.

Στο Bash, αλλά και άλλα shells καθώς και σε πολλές γλώσσες προγραμματισμού, ο χειρισμός κειμένου γίνεται με strings που ονομάζονται regular expressions - ή regex.

Αυτά είναι σκοτεινά και μυστηριώδη τέρατα, αλλά είναι εύκολα στην χρήση τους μιας και τα δαμάσετε. Στα regular expressions τα χαρακτηριστικά των πολύπλοκων "μοτίβων" κειμένου ορίζονται από μια τεράστια γκάμα μεταχαρακτήρων:

/linux/
/^linux/
/linux$/
/^linux.*inside$/

Περίεργο ε; Μην φοβάστε όμως. Το πρώτο regex απλώς σημαίνει πως αναζητιέται οποιαδήποτε γραμμή κειμένου που να περιέχει την λέξη 'linux' (ανεξάρτητα εάν είναι σε πεζά ή κεφαλαία ή εάν είναι μέρος μιας μεγαλύτερης λέξης). Η δεύτερη και η τρίτη είναι περισσότερο συγκεκριμένες. Θα ταιριάξουν μόνο γραμμές που ξεκινούν (^) ή τελειώνουν ($) με αυτή την λέξη. Το τελευταίο regex περιγράφει όλες τις γραμμές που ξεκινούν με την λέξη 'linux', τελειώνουν με το 'inside' και έχουν οποιοδήποτε (*) αριθμό χαρακτήρων (.) μεταξύ τους. Με λίγα λόγια θα ταιριάξει τα παρακάτω:

linuxinside
linux inside
Linux users love Linux Inside

Τα regular expressions χρησιμοποιούνται επίσης για την αντικατάσταση ενός μοτίβου κειμένου με ένα άλλο:

s/linux/Linux/
s/\d\d\d\d-12-25/2012-12-25/

To πρώτο regex μετατρέπει σε κεφαλαίο το πρώτο γράμμα όλων των λέξεων 'linux' σε ένα κείμενο ενώ το δεύτερο αντικαθιστά όλες τις ημερομηνίες των προηγουμένων Χριστουγέννων με την επόμενη. To '\d' είναι επίσης ένας μεταχαρακτήρας, που σημαίνει 'οποιοδήποτε ψηφίο', οπότε οι αυτοί οι τέσσερις θα ταιριάξουν οποιαδήποτε χρονολογία σε αυτή την μορφή.

Ο ρόλος του διερμηνέα

Τα regular expressions δίνονται ως παράμετροι σε μεμονωμένες εντολές (π.χ. sed) ή διερμηνείς (π.χ. ο bash) οι οποίοι μπορούν να τα θέσουν σε λειτουργία. Για την ευκολία μας όλα αυτά συνήθως γίνονται μέσα σε ένα script. Για να τρέξει όμως ένα script χρειάζεται πρώτα από όλα να του πούμε με ποιον διερμηνέα θα τρέξουν οι εντολές και που θα τον βρει στο σύστημα αρχείων.

Η τοποθεσία του εκτελέσιμου αρχείου ενός διερμηνέα μέσα στο σύστημα αρχείων ορίζεται ακριβώς μετά από το... shebang. Το shebang είναι οι δύο πρώτοι χαρακτήρες κάθε script - το χαριτωμένο #! ζευγάρι στην Unix φρασεολογία. Αυτοί οι χαρακτήρες "μαρκάρουν" το υπόλοιπο ως ένα script - με άλλα λόγια, μία σειρά από εκτελέσιμες εντολές προς τον διερμηνέα.

Άρα η πρώτη γραμμή #! /bin/bash ορίζει πως θέλετε το πρόγραμμα bash (που βρίσκεται στον κατάλογο bin) να εκτελέσει τις παρακάτω εντολές. Εάν το αρχείο που αναφέρετε στο shebang δεν υπάρχει ή δεν είναι διερμηνέας, το σύστημα απλώς θα επιστρέψει ένα 'command not found' μήνυμα λάθους και θα τερματίσει.

Οι δύο διερμηνείς που πιθανότατα να χρησιμοποιήσετε για τα regex σας είναι οι AWK και SED. Αυτά τα προγράμματα υπήρχαν από τις αρχές του Unix και μολονότι υπάρχουν και άλλα (π.χ. η Perl) πουν μπορούν να κάνουν πολύ περισσότερα, τα δύο αυτά προγράμματα είναι γρηγορότερα και γιαυτό χρησιμοποιούνται ακόμα σε scripts εκκίνησης του συστήματος.

Χρησιμοποιώντας την SED και την AWK

O SED λειτουργεί πάνω σε ροές κειμένου (το όνομα του σημαίνει Stream Editor). Φορτώνει μια γραμμή κειμένου κάθε φορά, την επεξεργάζεται σύμφωνα με τις εντολές που έχει λάβει και την εκτυπώνει στην τυπική έξοδο (STDIN):

cat somefile | sed '/^Α'

H παραπάνω εντολή θα διαγράψει όλες τις γραμμές που ξεκινούν με τον χαρακτήρα Α.

Η ΑWK ονομάστηκε με αυτό το παράξενο όνομα από τα επίθετα των προγραμματιστών της: Aho, Weinberger και Kernighan. Είναι κάπως πιο ισχυρή από την SED και δουλεύει με το ίδιο σκεπτικό. Μία γραμμή κά γθε φορά. Τυπικά κάθε γραμμή είναι και μια ξεχωριστή εγγραφή, η οποία αναφέρετε ως $0. Οι εγγραφές αποτελούνται συνήθως από πεδία χωρισμένα με κενά, αναφερόμενα ως $1, $2 κ.ο.κ.

awk '/fax/ { print }'/bin/*

Με αυτή την εντολή αναζητούνται και εκτυπώνονται όλες οι γραμμές που περιέχουν την λέξη 'fax' σε όλα τα αρχεία στον bin κατάλογο.

Μέχρι τώρα, στα παραδείγματα μας ασχοληθήκαμε με την αναζήτηση, μορφοποίηση και διαγραφή ξεχωριστών προτάσεων κειμένου. Ωστόσο υπάρχουν τρόποι να χρησιμοποιήσετε τη γραμμή εντολών του Linux για να αναζητήσετε ολόκληρα κομμάτια κειμένου. Τι κάνετε π.χ. όταν βρίσκετε μια αγγελία και θέλετε να την κρατήσετε στο πορτοφόλι σας; Την κόβετε με ένα ψαλίδι και πετάτε όλα τα υπόλοιπα προφανώς. Μπορείτε λοιπόν να προγραμματίσετε την γραμμή εντολών να κάνει ακριβώς αυτό χρησιμοποιώντας ροές κειμένου.

Εξάγοντας κομμάτια κειμένου

Τα τέσσερα περισσότερο χρήσιμα εργαλεία για αυτή την δουλεία είναι τα head, tail, cut και grep. Τα πρώτα δύο επιστρέφουν τις πρώτες ή τις τελευταίες γραμμές από ένα κείμενο. Με την παρακάτω εντολή παίρνετε ως αποτέλεσμα τις γραμμές κειμένου από την 16η έως την 20η από το somefile.txt:

head -20 somefile | tail -5

H cut εντολή κάνει ακριβώς το ίδιο αλλά κάθετα:

cut -c20,23 somefile.txt
ls -lrt | cut -c44-

To πρώτο παράδειγμα επιστρέφει μόνο τις στήλες από την 20η μέχρι την 23η του somefile.txt. Η δεύτερη παίρνει την λεπτομερειακή λίστα αρχείων (ls -lrt) και διαγράφει τα πάντα εκτός από την ημερομηνία δημιουργίας και το όνομα.

Τέλος υπάρχει και η οικογένεια των εντολών grep. Αυτές είναι στο Linux, τρεις διαφορετικές εντολές (grep, egrep, fgrep) οι οποίες μπορούν να εξάγουν από αρχεία όσες γραμμές ταιριάζουν σε ένα δοσμένο regex. Η κάθε παραλλαγή της grep έχει διάφορες επιλογές και καταλαβαίνει ένα περιορισμένο σύνολο regex δομών. Σε οποιαδήποτε περίπτωση, τα regex ταιριάσματα δεν μπορούν να καλύπτουν παραπάνω από μία γραμμή. Μερικές κλασικές χρήσεις της grep είναι οι εξής:

grep Linux *.txt
grep -i -v Windows *.txt
egrep 'Euro|Drachma' invoice*.txt

H πρώτη εντολή θα σας επιστρέψει όλες τις γραμμές που περιέχουν την λέξη 'Linux' σε όλα τα αρχεία επέκτασης .txt. Η δεύτερη εμφανίζει όλες τις γραμμές που ΔΕΝ περιέχουν (-v) την λέξη Windows, ανεξάρτητα εάν είναι σε κεφαλαία ή πεζά (-i). Τέλος, η τελευταία εντολή χρησιμοποιείτε εάν θέλετε να εμφανίσετε όλες τις γραμμές που περιέχουν είτε την λέξη Euro ή Drachma από όλα τα invoice αρχεία.

Το εργαλείο-έγγραφο 'Here'

Δουλεύοντας με κομμάτια κειμένου αναπόφευκτα θα συναντήστε τα έγγραφα 'Here'. Τα έγγραφα αυτά, εκμεταλλεύονται ένα πολύ χρήσιμο χαρακτηριστικό του κελύφους. Με τα 'here' έγγραφα μπορείτε να τοποθετήσετε ένα κομμάτι κειμένου κατευθείαν σε ένα script και να το χρησιμοποιήσετε είτε σαν την τυπική έξοδο μιας εντολής ή σαν αντιστοίχηση μεταβλητών.

Τα 'here' έγγραφα χρησιμοποιούν έναν εξειδικευμένο operator, <<, για τον ορισμό ενός κομματιού κειμένου. Η σύνταξη είναι αρκετά απλή:

cat <<END_OF_EMBEDDED_TEXT
Αγαπητέ $SUBSCRIBER
O λογαριασμός σας έληξε.
Παρακαλούμε στείλτε το $INVOICE στο περιοδικό το συντομότερο δυνατόν.
Ευχαριστούμε.
END_OF_EMBEDDED_TEXT

Όπως βλέπετε, το string μετά από τον << operator (END_OF_EMBEDDED_TEXT) είναι το ίδιο με αυτό που σημειώνει το τέλος του 'here' εγγράφου. Τώρα φανταστείτε πως ο παραπάνω κώδικας εκτελείτε σε έναν βρόγχο ο οποίος ανατρέχει στα περιεχόμενα μιας βάσης δεδομένων σε κείμενο. Ο κώδικας θα μπορούσε να δημιουργεί μια σειρά αιτήσεων πληρωμής με τα πραγματικά ονόματα των πελατών καθώς και τα ποσά που πρέπει ο κάθε ένας να πληρώσει. Η εκτύπωση ή η αποστολή μέσω email είναι εύκολη υπόθεση μετά. Μία άλλη καλή χρήση των 'here' εγγράφων είναι η δημιουργία προσωρινών αρχείων ή εντολών για αλληλεπιδραστικά προγράμματα όπως το FTP.

Πώς να βρείτε bookmarks που δεν ισχύουν πια

Το τελευταίο μέρος του tutorial είναι ένα χρήσιμο script. Βάζουμε στοίχημα πως έχετε εκατοντάδες - εάν όχι χιλιάδες - links στα bookmark αρχεία του browser σας. Υπάρχουν αρκετά καλές πιθανότητες πολλά από αυτά τα links να μην ισχύουν πλέον. Οι ιστοσελίδες μετακινούνται και εξαφανίζονται όλη την ώρα. Μπορείτε αμέσως να βρείτε ποια links είναι ανενεργά με το παρακάτω script.

Είναι φτιαγμένο για Mozilla bookmarks αλλά η τροποποί γηση του για άλλα format δεν θα πρέπει να είναι δύσκολη.

Για να το κατανοήσετε πλήρως το script, επιστρέψτε στα αρχικά μέρη του tutorial ώστε να θυμηθείτε τι κάνουν οι μεταχαρακτήρες.

 

 

#!/bin/bash
\rm url_list
\rm url_control_tmp
touch url_control_tmp
grep '<DT><A ' -f2 >url_list
for URL in `cat url_list`
 do
  echo -n $URL >>url_control_tmp
  curl --head $URL 2>/dev/null | grep 'Not Found' >>url_control_tmp
done
awk '{ print $1 }' url_control_tmp | sort | cat -n
exit

 

 

Οι πρώτες τρεις εντολές απλώς διαγράφουν (rm) τα όποια προσωρινά αρχεία δημιουργήθηκαν από τις προηγούμενες εκτελέσεις του script και μετά δημιουργούν ένα νέο, για λόγους που θα τους κατανοήσετε στην συνέχεια.

Μετά ξεκινά η διασκέδαση. Το αρχείο με τα bookmarks δίνετε ως πρώτη παράμετρο στο script οπότε το όνομα του περιέχετε στην $1 μεταβλητή. Στο αρχείο αυτό, οι γραμμές που περιέχουν links ξεκινούν με το <DT><A HREF=" string. To script τα εξάγει με την grep και ύστερα, χρησιμοποιώντας τα διπλά εισαγωγικά ως διαχωριστή πεδίων (cut '-d"') αγνοεί όλα τα υπόλοιπα εκτός από το δεύτερο πεδίο (-f2). Αυτό είναι το πραγματικό URL οπότε με αυτό τον τρόπο όλα τα links, ένα σε κάθε γραμμή και τίποτε άλλο, αποθηκεύονται στο url_list αρχείο. Η for εντολή εκτελεί μια επανάληψη σε κάθε γραμμή του url_list αρχείου, χάρης της cat εντολής. Μέσα στον βρόγχο for, η echo εντολή απλώς επισυνάπτει (append) σε ένα άλλο αρχείο, χωρίς newline (-n), το τρέχον URL. Για να λειτουργήσει αυτό, το αρχείο πρέπει να υπάρχει ήδη γι' αυτό και δημιουργήθηκε στην αρχή (touch).

To curl είναι ένα εξαίρετο εργαλείο για web browsing που δουλεύει από την γραμμή εντολών ώστε να ανακτά αυτόματα οποιαδήποτε δεδομένα από το Internet. Σε αυτό το παράδειγμα, εκτελείτε μία φορά για κάθε URL, αλλά κατεβάζει μόνο τις επικεφαλίδες του (-head). Οι επικεφαλίδες περιέχουν δεδομένα που αντιστοιχούν σε κάθε έγγραφό όπως:

HTTP/1.1 200 OK
Date: Fri, 14 Aug 2013 23:09:54 GMT
Server: Apache/2.1.1 (Unix) (Red Hat/Linux)
Content-Type: text/html

H σχετική γραμμή είναι η πρώτη. Το 200 ΟΚ σημαίνει πως η σελίδα είναι διαθέσιμη. Μία ανύπαρκτή σελίδα πιθανότατα να επέστρεφε κάτι παρόμοιο του 404 Not Found για αποτέλεσμα.

Κατά την εκτέλεση του curl, τα μηνύματα λάθους αγνοούνται. Η STDERR έχει τον αριθμό Ι/Ο ροής 2 (0 εισαγωγή, 1 εξαγωγή), οπότε το 2>/dev/null σημαίνει πως η ροή αυτή στέλνετε σε μία ψεύτικη συσκευή (/dev/null) που παρέχετε από το Unix για περιπτώσεις όπως αυτή.

Το grep κομμάτι της εντολής αποθηκεύει μόνο τις γραμμές που περιέχουν την κωδική HTTP λέξη 'not found' στο url_control_tmp.

H εντολή που ξεκινά με την awk τυπώνει μόνο την τιμή του URL (πρώτο πεδίο, $1) στην τυπική έξοδο. Η λίστα των αποτελεσμάτων μετέπειτα ταξινομείτε και εκτυπώνετε με έναν σειριακό αριθμό (cat -n).

Κατά τον εκτέλεση του script τα αποτελέσματα θα είναι κάπως έτσι:

1 http://…..

2 http://…..

3 http://…..

 

Αυτό το εκπληκτικό script λοιπόν, σας δείχνει πως η εκμάθηση των εντολών κελύφους όχι μόνο βελτιώνει τις προγραμματιστικές σας ικανότητες αλλά και την browsing εμπειρία σας. Εκτός από αυτό, είναι μια καλή σημείωση για το κλείσιμο του tutorial αυτού.

 

Δείτε περισσότερα

H απόλυτη πηγή για την γνώση των regular expressions είναι το βιβλίο Mastering Regular Expression από την O'Reilly (www.oreilly.com/catalog/regex2). Εισαγωγικά βοηθήματα μπορείτε να βρείτε στο www.zvon.org/other/PerlTutorial/Output/contents.html καθώς και μία εισαγωγή στους SED και AWK διερμηνείς στο www.faqs.org/docs/abs/HTML/sedawk.html. Όλες οι εντολές που αναφέρονται στο παρών άρθρο έχουν λεπτομερείς Unix man pages. Απλώς γράψτε :

man <όνομα εντολής>

 

Περισσότερα tutorials για Bash:

Εισαγωγή στο Bash: Ροές και pipes

Bash: Βρόχος επανάληψης σε εύρος αριθμών όπου μεταβλητές ορίζουν την αρχή ή το τέλος

Bash: Πως εκτελούμε μια εντολή Χ φορές στη σειρά με την for

Bash: Κωδικοί εξόδου και έλεγχος ροής

Scripting: Όταν το GUI δέν επαρκεί

Bash: Επεξεργασία κειμένου με Regular Expressions και άλλα κόλπα

 

Δώσε αστέρια!

MO: 5 (ψήφοι: 2)

Σχόλια

Γίνεται τρομερή δουλειά στο site από επιμορφωτικά θέματα για το linux .....

Μπράβο σε εσένα και στους άλλους που γράφουν τέτοια θέματα !!!

ΥΓ:

Θα συμβάλλω κι εγώ σε αυτήν την υπόθεση, μόλις ξεφρακάρω από τα διαβάσματα που σου έχω πει ....