Προγραμματισμός με gawk μέρος 1ο: Πράξεις
Description: This is issue 23 of Magaz, the Hellenic LinuxMagazine
Keywords: magaz hellenic greek hellas greece hellug linux magazine

Magaz, The Greek Linux Magazine
Τεύχος #23
Περιληπτικά Περιεχόμενα Αναλυτικά Περιεχόμενα Mirrors Links Πληροφορίες Συντάκτες Αναζήτηση Επικοινωνία
Προηγούμενο Περιεχόμενα ### 4. Πράξεις ### 4.1 Εισαγωγή Οι πράξεις είναι αυτές που λένε στην awk "τί να κάνει" όταν το πρότυπο ταιριάξει με αυτό που του ζητήσαμε. Μία πράξη αποτελείται από μία ή περισσότερες εντολές, οι οποίες μοιάζουν πάρα πολύ με τις εντολές τις C. Μία πράξη μπορεί να περιέχει: Μεταβλητές Πίνακες Ενσωματωμένες συναρτήσεις (αλφαριθμητικές και αριθμητικές) Εντολές ελέγχου ροής Εκτυπώσεις Οι πράξεις γράφονται μέσα σε αγκύλες ({}). Ο μόνος περιορισμός που έχετε, είναι να γράψετε την πρώτη αγκύλη στην ίδια γραμμή με το πρότυπο. Αυτός, βέβαια, είναι ο μόνος σας περιορισμός. Υπάρχουν κανόνες για τον τρόπο με τον οποίο γράφετε ένα awk script. Ευτυχώς, οι κανόνες αυτοί είναι σχεδόν ίδιοι με τους κανόνες της C: γενικά, στην συγγραφή ενός προγράμματος, έχετε μιά ελευθερία με τα κενά, τις γραμμές κλπ. Ας τα δούμε, όμως, από την αρχή. Μέσα σε ένα script, οι κενές γραμμές αγνοούνται. Οι εντολές που γράφετε μπορούν είτε να είναι στην ίδια γραμμή είτε σε διαφορετική. Η awk αναγνωρίζει τα κενά με τον ίδιο τρόπο που τα αναγνωρίζει η C. Οι επόμενες δύο γραμμές είναι ακριβώς οι ίδιες:
    $1=="Linux"{print"μήνυμα 1"}

    $1 == "Linux" { print "μήνυμα 1" }
Εννοείται πάντως, πως διαφόρων ειδών κενά μέσα στα εισαγωγικά δεν αγνοούνται. Τα υπόλοιπα κενά (μέσα στον κώδικα) αγνοούνται. Μπορείτε επίσης, να "σπάτε" τις εντολές σε πολλές γραμμές:
    $1  ==  "Linux"  {
                      print "μήνυμα 1"
                     }
Μπορείτε να έχετε πολλές διαφορετικές πράξεις μέσα στις αγκύλες ({}). Μπορείτε να τις χωρίζετε με το ελληνικό ερωτηματικό (;):
    $1 == "Linux" {print "μήνυμα 1"; print "μήνυμα 2"; print μήνυμα 3";}
Όμως, έχετε τη δυνατότητα να μην χρησιμοποιήσετε το ελληνικό ερωτηματικό, αλλά να γράψετε κάθε εντολή της πράξης σε διαφορετική γραμμή. Το ακόλουθο παράδειγμα έχει το ίδιο αποτέλεσμα με το παραπάνω:
    $1 == "Linux" {
                   print "μήνυμα 1"
                   print "μήνυμα 2"
                   print "μήνυμα 3"
                  }
Επίσης, μπορείτε να έχετε σχόλια (comments) μέσα στο script σας. Αυτό μπορείτε να το κάνετε χρησιμοποιώντας το σύμβολο # μπροστά από κάθε γραμμή σχόλιου. ### 4.2 Μεταβλητές Οι μεταβλητές σας χρειάζονται, φυσικά, για να έχετε τη δυνατότητα να αποθηκεύετε δεδομένα μέσα σε ένα πρόγραμμα. Υπάρχουν τριών ειδών μεταβλητές στην awk: Μεταβλητές πεδίου Προκαθορισμένες μεταβλητές Καθορισμένες από το χρήστη μεταβλητές Από όλα αυτά τα είδη έχουμε δεί παραδείγματα των πρώτων δύο ειδών. Τις μεταβλητές πεδίου, όπως το $1 και τις προκαθορισμένες μεταβλητές, όπως το NF κ.α. Οι μεταβλητές που ορίζονται από το χρήστη είναι, όπως θα καταλάβατε, οι μεταβλητές που δημιουργείτε εσείς μέσα σε ένα πρόγραμμα για να σας εξυπηρετήσουν σε έναν συγκεκριμένο σκοπό. Ένα πλεονέκτημα της awk είναι πως δεν χρειάζετε να ορίσετε τον τύπο της μεταβλητής που θα δημιουργήσετε. Δεν είναι π.χ. όπως στη C (και σε άλλες) που ορίζετε το αν μία συγκεκριμένη μεταβλητή είναι integer, float, char κλπ. Στην awk απλά χρησιμοποιείτε την μεταβλητή (θυμάστε την BASIC ? :-) H awk καταλαβαίνει τον τύπο της μεταβλητής, από το πώς αυτή χρησιμοποιείται μέσα στο πρόγραμμα. Αν για παράδειγμα, μέσα σε μιά μεταβλητή συμπεριλάβετε γράμματα και/ή αριθμούς, τότε αυτή η μεταβλητή αναγνωρίζεται ως αλφαριθμητικό. Αν χρησιμοποιήσατε μόνο αριθμούς, τότε αυτή αναγνωρίζεται ως αριθμητική μεταβλητή. Όπως και στη C, τα ονόματα των μεταβλητών που χρησιμοποιείτε ΠΡΕΠΕΙ να αρχίζουν με αλφαβητικό χαρακτήρα ή με μία κάτω παύλα/underscore (_). Για το υπόλοιπο όνομα της μεταβλητής μπορείτε να χρησιμοποιήσετε γράμματα, underscores ή αριθμούς. Οι αλφαβητικοί χαρακτήρες μπορούν να είναι είτε κεφαλαία είτε μικρά γράμματα. Φυσικά, σε αυτό το σημείο πάντα δίνονται δύο (κλασσικές) συμβουλές: Η μία είναι να δίνετε στις μεταβλητές ονόματα περιγραφικά (δηλαδή, να καταλαβαίνει κάποιος τί είναι το περιεχόμενό τους από το όνομά τους). Η δεύτερη συμβουλή είναι να αποφεύγετε όσο μπορείτε να γράφετε τις μεταβλητές με κεφαλαία γράμματα γιατί, όπως θα δούμε παρακάτω, οι προκαθορισμένες μεταβλητές της awk είναι με κεφαλαία γράμματα, οπότε μπορεί εύκολα να γίνει λάθος. Λοιπόν, ας δούμε τώρα έναν πίνακα με τις προκαθορισμένες (built-in variables) μεταβλητές της awk. Ο πίνακας έχει 3 στήλες. Στην 3η στήλη αναγράφεται, αν υπάρχει, η default τιμή μιας μεταβλητής. Επίσης, να τονίσω ότι πολλές από τις παρακάτω μεταβλητές δεν υπάρχουν σε προηγούμενες εκδόσεις της awk (άλλωστε μιλάμε για gawk):
Μεταβλητή | Σημασία | Default Τιμή
ARGC | Ο αριθμός των παραμέτρων που δίνονται στην γραμμή εντολών |
ARGV | Πίνακας με όλες τις παραμέτρους που δίνονται στην γραμμή εντολών |
CONVFMT | Η μορφή που η awk "διαβάζει" τους αριθμούς | %.6g
ENVIRON | Οι μεταβλητές περιβάλλοντος του UNIX |
ERRNO | Τα μηνύματα λάθους του UNIX |
FILENAME | Το όνομα του τρέχοντος αρχείου εισόδου |
FNR | Ο τρέχων αριθμός εγγραφής |
FS | Ο διαχωριστής πεδίων | Space
IGNORECASE | Ελέγχει την case sensitivity | 0 (δηλ. υπάρχει case sensitivity)
NF | Ο αριθμός των πεδίων στην τρέχουσα εγγραφή |
NR | Ο αριθμός των εγγραφών |
OFMT | Η μορφή εξόδου των αριθμών | %.6g
OFS | Ο διαχωριστής πεδίων για την έξοδο | Space
ORS | Ο διαχωριστής εγγραφών της εξόδου | Newline
RS | Ο διαχωριστής εγγραφών της εισόδου | Newline
- #### Αναλυτικά Η πρώτη παράμετρος που δίνετε στην γραμμή εντολών συμβολίζεται με ARGV[0], ενώ η τελευταία παράμετρος είναι η ARGV[ARGC-1]. Για την ακρίβεια, η πρώτη "παράμετρος" που δίνουμε στην γραμμή εντολών είναι η ίδια η gawk. Αρα ARGV[0]="gawk" Ας μιλήσουμε τώρα για εκείνη την παράμετρο -Fx που είδαμε στην σύνταξη της εντολής gawk. Εξ ορισμού, τα πεδία ξεχωρίζουν από τα κενά (ανεξάρτητου μήκους κενά) μεταξύ τους. Τί γίνεται, όμως, αν θέλετε να επεξεργαστείτε ένα αρχείο σαν το /etc/passwd το οποίο δεν περιέχει κενά, αλλά παρόλα αυτά περιέχει πολλά πεδία με πληροφορίες; Εδώ θα χρειαστεί να χρησιμοποιήσετε διαφορετικό από τον εξ ορισμού διαχωριστή πεδίων (field seperator). Αυτό μπορείτε να το κάνετε με την παράμετρο -Fx όπου στη θέση του x θα βάλετε τον διαχωριστή που θέλετε. Για παράδειγμα, στο αρχείο /etc/passwd, σίγουρα θα θέλετε τα πεδία να ξεχωρίζουν με το σύμβολο της άνω και κάτω τελείας (:). Επομένως, αρκεί να χρησιμοποιήσετε την παράμετρο -F: Υπάρχει, όμως, εναλλακτικός τρόπος να ορίσετε τον διαχωριστή. Η μεταβλητή FS χρησιμοποιείται από την awk για αυτόν ακριβώς το λόγο. Επομένως, έχετε τη δυνατότητα να χρησιμοποιήσετε αυτήν την μεταβλητή σε συνδυασμό με το ειδικό πρότυπο BEGIN. Π.χ. πρίν γράψετε οτοδήποτε άλλο στον κώδικά σας, μπορείτε να γράψετε το:
      BEGIN { FS = ":" }
- Θα καταλάβετε την μεταβλητή ENVIRON με το ακόλουθο παράδειγμα:
      ENVIRON["TERM"] == "vt100" { print "Δουλεύετε σε VT100 !!" }
Η παραπάνω εντολή ελέγχει αν είστε σε VT100 τερματικο και, αν είστε, τυπώνει το μήνυμα. Είδατε δύο Default τιμές να είναι "%.6g". Για το τί σημαίνει αυτή η τιμή (και γενικά τιμές τέτοιου τύπου) θα μιλήσουμε όταν αναφερθούμε στις εκτυπώσεις και, συγκεκριμένα, όταν αναφερθούμε στην "printf" (στο 2ο μέρος). ### 4.3 Αλφαριθμητικά Εδώ θα σας δώσω ένα παράδειγμα "μεταχείρισης" αλφαριθμητικών (άλλωστε, σε προηγούμενα παραδείγματα, θα έχετε ήδη καταλάβει τί/πώς/πού/κλπ. είναι τα αλφαριθμητικα). Στη συνέχεια, θα μιλήσουμε για τις αλφαριθμητικές ενσωματωμένες συναρτήσεις και στο τέλος θα σας δώσω έναν πίνακα με ειδικές αλφαριθμητικές σταθερές. Τα αλφαριθμητικά πάντα γράφονται μέσα σε ζευγάρι διπλών εισαγωγικών ("). Ένα αλφαριθμητικό μπορεί να περιέχει οποιουσδήποτε χαρακτήρες. Να σημειώσω πως αν χρησιμοποιήσετε ΜΟΝΟ αριθμούς (π.χ. το "156") για ένα αλφαριθμητικό, τότε αυτόματα η awk το χρησιμοποιεί σαν αριθμητική μεταβλητή. Αντίθετα, αν έχετε ένα αλφαριθμητικό (με χαρακτήρες ή ακόμα και "μορφοποιημένο" αριθμό: π.χ. "+30 972 66 55 44" <-- το οποίο για την gawk είναι αλφαριθμητικό, αφού περιέχει κενά (" "). Προσέξτε, το "+" επιτρέπεται, όπως και το "-" σε αριθμητικές μεταβλητές) και προσπαθήσετε να κάνετε μιά αλγεβρική πράξη με αυτό (πρόσθεση, αφαίρεση, κλπ ...) τότε η awk θα προσπαθήσει να μετατρέψει το αλφαριθμητικό σε αριθμητική μεταβλητή και, πολύ απλά, θα του δώσει την αριθμητική τιμή 0 (μηδέν). Ας δούμε το παράδειγμα:
    $ gawk 'BEGIN {x="αβγ""δεζ" ; y = "magaz"; z=x y; z2 = "A"x"LINUX"y"END"; print x, y, z, z2}'
    αβγδεζ magaz αβγδεζmagaz AαβγδεζLINUXmagazEND
Είδατε, λοιπόν, πως μπορούμε να "συνδυάσουμε" αλφαριθμητικά. Είδατε επίσης, στη μεταβλητή x, πώς δεν είναι ανάγκη να συμπεριλάβουμε όλους τους χαρακτήρες στο ίδιο ζεύγος εισαγωγικών. Ακόμα, στο τέλος, αν δεν είχαμε χρησιμοποιήσει το κόμμα (,) ανάμεσα στις μεταβλητές στην εντολή print, αυτό που θα μας έδινε η έξοδος θα ήταν: αβγδεζmagazαβγδεζmagazAαβγδεζLINUXmagazEND Ας δούμε τώρα τον πίνακα με τις συναρτήσεις. Και πάλι, μερικές από αυτές μπορεί να μην δουλεύουν σε παλιότερες εκδόσεις awk:
Συνάρτηση | Ερμηνεία
index(s1, s2) | Επιστρέφει την θέση (από αριστερά) του αλφαριθμητικού s2 στο αλφαριθμητικό s1
length(string) | Μετρά τον αριθμό χαρακτήρων του string
split(string, πίνακας, διαχωριστής) | Δείτε παρακάτω, στα αναλυτικά
substr(string, m, n) | Επιστρέφει εκείνο το τμήμα του αλφαριθμητικού string που αρχίζει από τη θέση m και έχει n χαρακτήρες
tolower(string) | Επιστρέφει το string με μικρούς χαρακτήρες
toupper(string) | Επιστρέφει το string με κεφαλαίους χαρακτήρες
- #### Αναλυτικά Η split(string, πίνακας, διαχωριστής) καταχωρεί τα τμήματα του αλφαριθμητικού string που χωρίζονται μεταξύ τους με τον χαρακτήρα "διαχωριστή", στα στοιχεία πίνακας[1], πίνακας[2], ... , πίνακας[ν] του πίνακα και επιστρέφει το ν. Αν δεν υπάρχει ο "διαχωριστής", τότε χρησιμοποιείται η μεταβλητή FS. Παράδειγμα:
      number = split("29/3/2000", array, "/")
- Λοιπόν, μετά την εκτέλεση της εντολής θα έχουμε: array[1]=29, array[2]=3, array[3]=2000. H μεταβλητή number θα έχει την τιμή 3. Ας δούμε και ένα παράδειγμα για την substr. Έχουμε:
      var=substr("Linux", 3, 2)
Μετά την εκτέλεση της εντολής, η μεταβλητή var θα περιέχει το αλφαριθμητικό "nu". Παρακάτω, βλέπετε έναν πίνακα με ειδικές αλφαριθμητικές σταθερές, τις οποίες μπορείτε να χρησιμοποιήσετε μέσα σε ένα αλφαριθμητικό για συγκεκριμένες, εξειδικευμένες "εκτυπώσεις". Π.χ. αν θα θέλατε μέσα σε ένα αλφαριθμητικό να χρησιμοποιήσετε τα διπλά εισαγωγικά (") τότε θα πρέπει να γράψετε πρίν από αυτά την backslash (\) ώστε να μην "μπερδευτεί" η awk με τα εισαγωγικά που περιβάλλουν το αλφαριθμητικό:
Σταθερά | Σημασία
\\ | Παριστάνει την ίδια την backslash
\a | Ο χαρακτήρας alert ή bell (Θα ηχήσει το speaker)
\b | Backspace
\f | Formfeed
\n | Newline
\r | Carriage return
\t | Tab
\v | Vertical Tab
\" | Παριστάνει τα εισαγωγικά
\xYY | Δείχνει ότι ο ΥΥ είναι δεκαεξαδικός αριθμός
\0YYY | Δείχνει ότι ο ΥΥΥ είναι οκταδικός αριθμός
### 4.4 Πίνακες Όταν έχετε πολλά δεδομένα (π.χ. τιμές) που αφορούν στο ίδιο θέμα, τότε έχετε 2 επιλογές: είτε θα χρησιμοποιήσετε πολλές μεταβλητές, είτε θα χρησιμοποιήσετε έναν πίνακα, ο οποίος σας επιτρέπει να έχετε συγκεντρωμένα πολλά δεδομένα. Ένας πίνακας αποτελείται από το όνομά του και τα στοιχεία του. Συμβολίζεται κάπως έτσι: όνομα[στοιχείο]. Προσοχή!! Οι πίνακες στην awk έχουν ειδικές ικανότητες, οι οποίες δεν παρουσιάζονται στις περισσότερες γλώσσες προγραμματισμού. Είναι δυναμικοί πίνακες, πράγμα που σημαίνει πως ξεφεύγουμε από το μαθηματικό μοντέλο του πίνακα (αν και πρέπει πάντα να το έχουμε στο μυαλό μας) καταργώντας τα "αριθμημένα" στοιχεία, δηλαδή τις διαστάσεις του πίνακα. Το στοιχείο του πίνακα στην awk συμβολίζεται με αλφαριθμητικό!! Δεν χρειάζετε να δηλώνετε για μία συγκεκριμένη μεταβλητή πως είναι πίνακας (όπως κάνουμε στη C), οπότε δεν δηλώνουμε εκ των προτέρων το μέγεθος που έχει ο πίνακάς μας. Όταν χρησιμοποιείτε ένα καινούριο στοιχείο για πρώτη φορά, τότε αυτό δυναμικά προστίθεται στον πίνακά μας. Ας δούμε δύο παραδείγματα πινάκων:
    magaz_subject["Linux"] = 1
    magaz_subject["gardening"] = 0
Αν θα θέλαμε να κάνουμε το ίδιο σε άλλη γλώσσα προγραμματισμού, τότε θα έπρεπε να έχουμε 2 διαφορετικούς πίνακες, όπου στον έναν να είχαμε διάφορα θέματα και στον άλλον πίνακα, στα ίδια στοιχεία να βάζαμε την τιμή 0 ή 1 (ανάλογα αν ανταποκρίνεται το magaz στα θέματα αυτά), οπότε κάθε φορά να κάναμε "αντιστοίχιση" των πινάκων. Δηλαδή, για το παραπάνω παράδειγμα, ο ένας πίνακας ας υποθέσουμε ότι στη θέση 5 (στοιχείο 5 / array[5]) περιείχε το αλφαριθμητικό "Linux" και στη θέση 6 το αλφαριθμητικό "gardening". Ο δεύτερος πίνακας θα είχε στη θέση 5 το 1 και στη θέση 6 το 0. Η τακτική αυτή στην awk (δηλαδή η χρήση αλφαριθμητικού για στοιχείο) είναι "λιγότερο προγραμματιστική-μαθηματική" αλλά ευκολότερη στο διάβασμα. Η τακτική αυτή οφείλεται για το γεγονός ότι η awk δεν υποστηρίζει άμεσα πολυδιάστατους πίνακες. #### Συναρτήσεις πινάκων Η awk περιλαμβάνει 2 συναρτήσεις για τους πίνακες: την in και την delete. Η συνάρτηση in ελέγχει την ύπαρξη ενός στοιχείου του πίνακα. Η delete σβήνει ένα στοιχείο από έναν πίνακα. Για παράδειγμα, μπορούμε να χρησιμοποιήσουμε την παρακάτω εντολή μέσα σε έναν βρόγχο if (τους οποίους θα δούμε στο 2ο μέρος) για να εξετάσουμε μιά ύπαρξη και να εκτελέσουμε, ανάλογα, μιά πράξη:
    "Linux" in magaz_subject
Ομοίως, χρησιμοποιούμε την συνάρτηση delete:
    delete magaz_subject["gardening"]
Προσέξτε, ότι όταν χρησιμοποιείτε την delete, το στοιχείο σβήνεται από την περιοχή μνήμης. Τα δεδομένα δεν μπορούν να ανακτηθούν. Προσέξτε όμως, πως δεν μπορείτε να χρησιμοποιήσετε την delete κάπως έτσι: delete magaz_subject. Δεν μπορείτε να σβήσετε ολόκληρο τον πίνακα, αλλά μόνο τα στοιχεία του. #### Πολυδιάστατοι Πίνακες Είπαμε πως η awk δεν υποστηρίζει άμεσα πολυδιάστατους πίνακες, αλλά τους "εξομοιώνει". Το γεγονός αυτό, δεν σας επηρεάζει ως προγραμματιστές. Εσείς προχωράτε κανονικά και δηλώνετε τους πολυδιάστατους πίνακες όπως ακριβώς και στη C, δηλαδή:
    magaz_subject[6, 2] = "Linux"
Εννοείται πως μπορείτε να χρησιμοποιήσετε τις in και delete όπως ακριβώς και στους μονοδιάστατους. Προηγούμενο Περιεχόμενα