💾 Archived View for magaz.hellug.gr › 23 › 03_gawk1 › index.gmi captured on 2024-02-05 at 09:39:49. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
DJ Art(mailto:djart@hellug.gr) Απρ 2000
Το άρθρο αυτό έχει σκοπό να σας βοηθήσει στην εκμάθηση της γλώσσας προγραμματισμού awk. Μία "εξειδικευμένη" γλώσσα προγραμματισμού, ένα ιδιαίτερα δυνατό εργαλείο στη διαχείρηση αρχείων κειμένου (text files), καθώς και δημιουργίας αναφορών, με το πλεονέκτημα ότι είναι και εύκολη στην εμάθηση και στην σύνταξη, αλλά και ότι "συγγενεύει" με την C. Το πλήρες άρθρο αποτελείται από δύο μέρη, εδώ σας παρουσιάζεται το πρώτο, ενώ ετοιμάζεται και το 2ο :-)
1. Εισαγωγή
2. Τα πρώτα βήματα
3. Πρότυπα (και Πράξεις)
4. Πράξεις
Η gawk, ή καλύτερα η GNU awk, είναι μιά από τις πιο καινούριες εκδόσεις της γλώσσας προγραμματισμού awk, η οποία είχε δημιουργηθεί για το περιβάλλον UNIX από τους Alfred Aho, Peter Weinberger και Brian Kernighan το 1977. Το όνομα awk προέρχεται από τα αρχικά γράμματα των επωνύμων των παραπάνω δημιουργών. Η σχεδίαση της awk φαίνεται στη διατριβή τους: "AWK - a pattern scanning and processing language" Software-Practice and Experience, Ιούλιος 1978. Η awk έχει την καταγωγή της σε ιδέες από την SNOBOL4, από την εντολή sed, από τα εργαλεία yacc και lex και από τη C. Επίσης, ο Kernighan είναι ένας από αυτούς που βοήθησε στη δημιουργία της C και του UNIX, ενώ οι άλλοι δύο βοήθησαν στη δημιουργία του UNIX. Για ακριβώς αυτούς τους λόγους, θα δείτε πολλές ομοιότητες μεταξύ της C και της awk. Υπάρχουν πολλές εκδόσεις της awk: η αυθεντική awk, η nawk, η POSIX awk και, φυσικά, η gawk, η οποία είναι βασισμένη στην POSIX awk.
H awk, σε όλες τις εκδόσεις, είναι μια γλώσσα επεξεργασίας και σύγκρισης προτύπων (patterns) με μεγάλη δύναμη σε αυτόν τον τομέα. Μπορεί να ψάξει ένα ή περισσότερα αρχεία για εγγραφές (records) που ταιριάζουν μέ ένα δοσμένο πρότυπο. Όταν ταιριάξουν, μία καθορισμένη πράξη (action) εκτελείται. Σαν προγραμματιστής, δεν χρειάζεται να ανησυχείτε για το άνοιγμα, την επαναλαμβανόμενη ανάγνωση των εγγραφών ή το κλείσιμο ενός αρχείου. Αυτές οι λεπτομέρειες γίνονται αυτόματα για εσάς. Το να γράφετε μικρά awk προγράμματα είναι εύκολο λόγω της λειτουργικότητας της γλώσσας, πολλές διαδικασίες είναι αυτοματοποιημένες.
Θα βρείτε πολλές εφαρμογές για τη awk, όπως το να "διαβάζετε" δεδομένα από ένα αρχείο, να μετράτε συγκεκριμένες "υπάρξεις" μέσα σ' αυτό, αλλά και να δημιουργείτε εξειδικευμένες αναφορές (reports). Η γενική σύνταξη της awk μοιάζει πάρα πολύ/έχει πολλά κοινά σημεία με τη C. Εάν ήδη ξέρετε C, τότε γνωρίζετε αρκετά πράγματα από τη awk. Από πολλές απόψεις, η gawk είναι μία ευκολότερη "έκδοση" της C. Αν δεν γνωρίζετε C, τότε θα την μάθετε ευκολότερα αν ήδη γνωρίζετε awk.
ΣΗΜΕΙΩΣΗ: H awk δουλεύει με text αρχεία, όχι εκτελέσιμα. Επειδή τα εκτελέσιμα αρχεία περιέχουν χαρακτήρες που μοιάζουν με το τέλος εγγραφών (δηλαδή σύμβολα αλλαγής σειράς), η awk μπερδεύεται. Αν θέλετε να επεξεργαστείτε εκτελέσιμα αρχεία, τότε χρειάζεστε την Perl ή, φυσικά, μία από τις "πατροπαράδοτες" γλώσσες προγραμματισμού, όπως η C.
Η awk είναι ευλύγιστη, όπως το UNIX περιβάλλον, περιλαμβάνει προκαθορισμένες μεταβλητές, αυτοματοποιεί πολλές από τις "διαδικασίες" του κλασσικού προγραμματισμού, παρέχει τη δυνατότητα των συμβατικών μεταβλητών, υποστηρίζει το μορφοποιημένο output της C και είναι εύκολη στη χρήση. Σας επιτρέπει να συνδυάσετε τα πλεονεκτήματα των shell scripts με πολλά στοιχεία της C. Να σημειώσω πως δεν χρειάζετε να ορίζετε τον τύπο μιάς μεταβλητής (σε αντίθεση με τη C), αφού η awk καταλαβαίνει τον τύπο τους όταν τις χρησιμοποιεί. Φυσικά, υποστηρίζει τους βρόγχους της C (if/else, do/while, for), αλλά δεν υποστηρίζει το switch/case. Παρόλα αυτά, υποστηρίζει το printf() της C, αλλά και την εντολή print, μιά απλούστερη (από την printf) εντολή.
Για να χρησιμοποιήσετε την gawk, θα πρέπει να γράψετε ένα πρόγραμμα. Το πρόγραμμα αυτό θα το ονομάζουμε awk script. Το λέω αυτό, γιατί σε απλές εφαρμογές θα παρατηρήσετε πως θα μπορούσατε αντί για την gawk να χρησιμοποιήσετε το grep. Στο grep, όμως, δεν γράφετε πρόγραμμα (script), αλλά του δίνετε παραμέτρους. Φυσικά, το awk script δεν έχει περιορισμούς στο μέγεθος. Μπορείτε να το κάνετε όσο μεγάλο θέλετε. To πρόγραμμα που φτιάχνετε μπορεί να "διαβαστεί" από την gawk με πολλούς τρόπους, από τους οποίους οι πιό συνηθισμένοι είναι η από την γραμμή εντολών ή από ένα αρχείο. Η gawk μπορεί επίσης να δεχθεί input με pipe (διασωλήνωση) από άλλο πρόγραμμα ή ακόμα και από το ίδιο το πληκτρολόγιο (επί τόπου). Το output με τη σειρά του, μπορεί εκτός από το standard, να ανακατεθυνθεί σε ένα αρχείο ή να διασωληνωθεί σε ένα άλλο πρόγραμμα.
gawk -Fx 'πρόγραμμα' [αρχείο/α] gawk -Fx -f αρχείο_εντολών [αρχείο/α]
Το πρόγραμμα είναι μία σειρά από εντολές της awk, ενώ αρχείο_εντολών είναι ένα αρχείο που περιέχει εντολές της awk. To αρχείο/α (στο τέλος της σύνταξης) είναι το όνομα του αρχείου στο οποίο θα γίνει η επεξεργασία που θέλουμε. Αν παραλείψουμε εκεί το αρχείο, τότε η gawk δέχεται input από την καθιερωμένη μονάδα εισόδου (πληκτρολόγιο).
Κάθε εντολή της awk έχει την μορφή: πρότυπο {πράξη}
Η awk "σαρώνει" γραμμή προς γραμμή το αρχείο και κάθε γραμμή συγκρίνεται με το πρότυπο. Αν ταιριάζει, τότε εκτελείται η αντίστοιχη πράξη. Αν από την παραπάνω μορφή απουσιάζει το πρότυπο, τότε η πράξη εκτελείται για κάθε γραμμή, ενώ αν απουσιάζει η πράξη, τότε όλες οι γραμμές που ταιριάζουν με το πρότυπο εμφανίζονται στην οθόνη. Την χρησιμότητα της επιλογής -Fx θα την δούμε παρακάτω.
Ο απλούστερος τρόπος να χρησιμοποιήσετε την awk είναι να γράψετε το πρόγραμμα στην γραμμή εντολών. Ας δούμε ένα απλό παράδειγμα:
$ cat arxeio.txt | gawk '{print NF ": " $0}' 2: Γεια σας 8: Σήμερα θα μάθουμε να προγραμματίζουμε με την gawk 4: Σας εύχομαι καλή επιτυχία!
Για να δούμε τώρα τί κάνει το πρόγραμμα. Το αποτέλεσμα της εντολής cat arxeio.txt περνά σαν είσοδος στην gawk. Πρότυπο δεν έχουμε, επομένως η πράξη (μέσα στις αγκύλες) εφαρμόζεται για κάθε γραμμή (του arxeio.txt). Από εκεί και πέρα η gawk εμφανίζει στην οθόνη των αριθμό των λέξεων, την άνω και κάτω τελεία, το κενό και μετά τα περιεχόμενα (ανά γραμμή) του arxeio.txt. To NF είναι μια προκαθορισμένη μεταβλητή της gawk, η οποία παριστάνει των αριθμό των πεδίων σε κάθε εγγραφή. Από default το κάθε πεδίο ξεχωρίζει από το άλλο με κενό (space) ή με tab, ενώ κάθε εγγραφή ξεχωρίζει με τον χαρακτήρα αλλαγής γραμμής. Επομένως, ας πώ τί κάνει η NF με πιο απλά λόγια: παριστάνει τον αριθμό λέξεων σε κάθε γραμμή. Όντως, στην πρώτη γραμμή η λέξεις ήταν 2 κλπ. Το σύμβολο $0 παριστάνει ολόκληρη την εγγραφή, ενώ υπάρχουν τα σύμβολα $1, $2 κ.ο.κ. για τα πεδία. Για παράδειγμα, στην πρώτη γραμμή, το $1 παριστάνει το πεδίο "Γεια", ενώ το $2 το "σας". Στην δεύτερη γραμμή το $5 είναι το "προγραμματίζουμε".
Θα μπορούσατε να χρησιμοποιήσετε την ανακατεύθυνση του UNIX, ή απλά το όνομα του αρχείου για να καταλήξετε στο ίδιο αποτέλεσμα με την παραπάνω εντολή. Δείτε άλλες δύο ισοδύναμες (με την παραπάνω) εντολές:
$ gawk '{print NF ": " $0}' < arxeio.txt $ gawk '{print NF ": " $0}' arxeio.txt
Ας δούμε πάλι το παραπάνω παράδειγμα, την εντολή gawk '{print NF ": " $0}' arxeio.txt. Θα μπορούσατε να απόφυγετε την πληκτρολόγηση της εντολής στην γραμμή εντολών, αλλά να την συμπεριλάβετε σε ένα awk script. Προσέξτε πως στο awk script δεν πρέπει να γράψετε τα απλά εισαγωγικά, αφού αυτά χρησιμεύουν για να μην ασχοληθεί ο φλοιός (shell) με το περιεχόμενό τους. Αν τα χρησιμοποιήσετε η gawk θα σας βγάλει ένα μήνυμα λάθους και, φυσικά, δεν θα έχετε το επιθυμητό αποτέλεσμα. Οπότε, θα δημιουργήσετε ένα αρχείο script.awk που θα περιέχει τα εξής:
{print NF ": " $0}
Στη συνέχεια, θα τρέξετε το πρόγραμμα με την εξής εντολή:
gawk -f script.awk arxeio.txt
Να αναφέρω επίσης, πως η gawk και άλλες εκδόσεις της awk, οι οποίες συμβαδίζουν με το POSIX πρότυπο (standard) σας επιτρέπουν να χρησιμοποιήσετε περισσότερα του ενός scripts μετά την επιλογή -f (π.χ. gawk -f script1 script2 script3 arxeio.txt). Θα σας συμβούλευα να προτιμήσετε να καλέσετε ένα μόνο script (και όχι πιο πολλά) για να αποφύγετε διάφορα λάθη ή ενέργειες που μπορεί να σας μπερδέψουν.
Στην gawk μπορείτε να χρησιμοποιήσετε πρότυπα 3 ειδών:
1. Ειδικά πρότυπα (special patterns)
2. Κανονικές παραστάσεις (regular expression patterns)
3. Σχεσιακές εκφράσεις (comparison patterns)
Η awk περιλαμβάνει δύο ειδικά πρότυπα: το BEGIN και το END. Η πράξη που ακολουθεί το πρότυπο BEGIN εκτελείται πρίν διαβαστεί οποιαδήποτε γραμμή του αρχείου. Για αυτόν τον λόγο, το BEGIN χρησιμοποιείται συνήθως για εκτύπωση επικεφαλίδων ή για αρχική καταχώρηση σε μία μεταβλητή. Η πράξη που ακολουθεί το END εκτελείται όταν έχουν επεξεργαστεί όλες οι γραμμές από το αρχείο.
Τα πρότυπα BEGIN και END είναι εντελώς προαιρετικά. Τα χρησιμοποιείτε αν θέλετε. Όμως, αν τα χρησιμοποιήσετε τότε μπορείτε να τα έχετε μόνο μιά φορά μέσα στο πρόγραμμά σας. Μην χρησιμοποιήσετε δύο BEGIN ή δύο END. Παράδειγμα για τα BEGIN και END θα δούμε παρακάτω, όταν μιλήσουμε για μεταβλητές.
Κανονική παράσταση είναι μία σειρά χαρακτήρων που περικλείονται ανάμεσα σε ζευγάρι από / (slash). Για παράδειγμα, αν εκτελέσετε την:
gawk '/linux/' my_doc.txt
τότε θα εμφανιστούν στην οθόνη όλες οι γραμμές του αρχείου my_doc.txt που περιέχουν τη λέξη linux.
Οι κανονικές παραστάσεις της gawk αποτελούνται από χαρακτήρες (όπως τα γράμματα της αλφαβήτου) και από μεταχαρακτήρες (metacharacters). Οι μεταχαρακτήρες είναι και αυτοί χαρακτήρες που έχουν ειδικό νόημα: δεν χρησιμοποιούνται για "ταίριασμα" με πεδίο ή εγγραφή ενός αρχείου, αλλά εκτελούν μιά συγκεκριμένη λειτουργία. Ας δούμε, λοιπόν, έναν πίνακα με τους μεταχαρακτήρες και την συμπεριφορά τους:
--------------------------------------------------------------------------------
\ Συμπεριφορά Μεταχαρακτήρας
\ Ακυρώνει την έννοια του ειδικού χαρακτήρα που ακολουθεί την backslash,
οπότε ο ειδικός χαρακτήρας αντιμετωπίζεται σαν απλός
^ Σύγκριση (matching) στην αρχή της γραμμής
$ Σύγκριση στο τέλος της γραμμής
/^$/ Σύγκριση για κενή γραμμή
. Συγκρίνει οποιονδήποτε απλό χαρακτήρα
[ΑΒΓ] Συγκρίνει για οποιονδήποτε από τους Α, Β ή Γ
[Α-Γα-γ] Συγκρίνει για οποιονδήποτε από τους Α, Β, Γ, α, β, ή γ
[^ΑΒΓ] Συγκρίνει για οποιονδήποτε άλλο χαρακτήρα εκτός των Α, Β ή Γ
Linux|System Συγκρίνει για την λέξη Linux ή για την System (λογικό OR)
[ΑΒΓ][ΔΕΖ] Συγκρίνει για τους Α, Β ή Γ, οι οποίοι όμως,θα πρέπει να ακολουθούνται
από οποιονδήποτε των Δ, Ε ή Ζ
[XYZ]* Συγκρίνει για καμμία ή περισσότερες εμφανίσεις της παράστασης[XYZ]
[XYZ]+ Συγκρίνει για μία ή περισσότερες εμφανίσεις της παράστασης[XYZ]
[XYZ]? Συγκρίνει για καμμία ή μία εμφάνιση της παράστασης[XYZ]
() Συνδυάζει κανονικές παραστάσεις. Π.χ. το (ανα|δια)λύω συγκρίνει για να
βρεί είτε τη λέξη αναλύω είτε τη λέξη διαλύω
--------------------------------------------------------------------------------
Ας δούμε την παρακάτω κανονική παράσταση:
/[Mm]agaz|interesting$/
Αυτή η παράσταση αναζητά τις γραμμές στις οποίες βρίσκονται οι λέξεις Magaz ή magaz (σε οποιοδήποτε σημείο της γραμμής) ή interesting (στο τέλος της γραμμής).
Ας δούμε άλλη μία παράσταση:
/[Mm][Aa][Gg][Aa][Zz]/
Αυτή η παράσταση αναζητά τις γραμμές στις οποίες βρίσκεται η λέξη magaz ΜΕ ΟΠΟΙΟΝΔΗΠΟΤΕ τρόπο γραμμένη, δηλαδή μπορεί να "ξεθάψει" την λέξη ακόμα και αν αυτή είναι γραμμένη με έναν από τους συνολικά 32 τρόπους!! (δηλ. Magaz, MAgaz, MAGaz, MAGAz, MAGAZ, mAgaz κ.ο.κ.)
Με τις σχεσιακές εκφράσεις γίνεται σύγκριση αριθμών ή αλφαριθμητικών (strings). H σύγκριση γίνεται με τους σχεσιακούς τελεστές, οι οποίοι είναι παρόμοιοι με αυτούς της C και των φλοιών (shells) του UNIX. Μέχρι τώρα είδαμε πως οι κανονικές παραστάσεις "σαρώνουν" όλη τη γραμμή για να βρούν αυτό που ζητάμε. Μπορούμε, όμως, να συνδυάσουμε τις κανονικές παραστάσεις με τις σχεσιακές εκφράσεις για να "ψάξουμε" σε συγκεκριμένα πεδία. Ας δούμε πρώτα όμως, έναν πίνακα με τους σχεσιακούς τελεστές:
--------------------------------------------------------------------------------
\ Ερμηνεία Τελεστής
== Ίσο με
< Μικρότερο
Μεγαλύτερο
<= Μικρότερο ή ίσο
= Μεγαλύτερο ή ίσο
!= Διάφορο του
~ Ίσο με την ακόλουθη κανονική παράσταση
!~ Διάφορο της ακόλουθης κανονικής παράστασης
--------------------------------------------------------------------------------
Ας δούμε ένα "τυπικό" παράδειγμα εφαρμογής των παραπάνω τελεστών:
$1 == "Linux" { print "Linux Rocks!!" }
Η παραπάνω εντολή ελέγχει αν το πρώτο πεδίο είναι η λέξη Linux και αν είναι εκτελεί την πράξη, δηλαδή στην περίπτωσή μας εμφανίζει στην οθόνη το μήνυμα Linux Rocks!! Προσέξτε ότι εδώ τα πράγματα είναι όπως ακριβώς φαίνονται, δηλαδή αν το πρώτο πεδίο είναι η λέξη linux (το l όχι κεφαλαίο όπως το δηλώσαμε), τότε δεν θα εκτελεστεί η πράξη. Επομένως, ας δούμε τη χρησιμότητα του συνδυασμού της κανονικής παράστασης με τις σχεσιακές εκφράσεις:
$6 !~ /[Mm][Aa][Gg][Aa][Zz]/ { print "we have no magaz" }
Η παραπάνω εντολή ελέγχει αν το 6ο πεδίο περιέχει τη λέξη magaz (όπως και αν είναι γραμμένη) και όταν δεν τη βρεί τυπώνει αυτό που περιέχεται στα εισαγωγικά της print.
Οι λογικοί τελεστές που χρησιμοποιούνται στην awk είναι παρόμοιοι με αυτούς της C. Χρησιμοποιούμε τους τελεστές αυτούς για να συνδυάσουμε πρότυπα (σχεσιακές εκφράσεις ή κανονικές παραστάσεις) ώστε να "συνθέσουμε" μιά πολύπλοκη μορφή λογικής. Ας δούμε έναν πίνακα με τους λογικούς τελεστές:
--------------------------------------------------------------------------------
\ Ερμηνεία Τελεστής
&& Λογικό ΚΑΙ (AND)
|| Λογικό Η (OR)
! Λογικό ΟΧΙ (NOT)
--------------------------------------------------------------------------------
Ας δούμε ένα παράδειγμα:
$3 == "Linux" && $18 == "Magaz" {print "Μήνυμα εξόδου")
Εδώ, το πρότυπό μας, στην περίπτωση που το 3ο πεδίο είναι η λέξη Linux ΚΑΙ το 18ο πεδίο η λέξη Magaz, τυπώνει στην οθόνη το μήνυμα.
Μπορείτε στη θέση ενός προτύπου να χρησιμοποιήσετε ένα διάστημα προτύπων. Το διάστημα προτύπων είναι δύο πρότυπα, τα οποία χωρίζονται με κόμμα (,) και χρησιμεύουν στο εξής: Η πράξη που τα ακολουθεί, εφαρμόζεται σε όλες εκείνες τις γραμμές που βρίσκονται ανάμεσα στη γραμμή που εμφανίζεται το πρώτο πρότυπο και στη γραμμή που εμφανίζεται το δεύτερο πρότυπο. Παράδειγμα:
/Linux/,/Magaz/
Η gawk θα αναζητήσει εκείνες τις γραμμές, από εκείνη που εμφανίζεται η λέξη Linux μέχρι εκείνη που εμφανίζεται η λέξη Magaz.
Οι πράξεις είναι αυτές που λένε στην 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 σας. Αυτό μπορείτε να το κάνετε χρησιμοποιώντας το σύμβολο # μπροστά από κάθε γραμμή σχόλιου.
Οι μεταβλητές σας χρειάζονται, φυσικά, για να έχετε τη δυνατότητα να αποθηκεύετε δεδομένα μέσα σε ένα πρόγραμμα. Υπάρχουν τριών ειδών μεταβλητές στην 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ο μέρος).
Εδώ θα σας δώσω ένα παράδειγμα "μεταχείρισης" αλφαριθμητικών (άλλωστε, σε προηγούμενα παραδείγματα, θα έχετε ήδη καταλάβει τί/πώς/πού/κλπ. είναι τα αλφαριθμητικα). Στη συνέχεια, θα μιλήσουμε για τις αλφαριθμητικές ενσωματωμένες συναρτήσεις και στο τέλος θα σας δώσω έναν πίνακα με ειδικές αλφαριθμητικές σταθερές.
Τα αλφαριθμητικά πάντα γράφονται μέσα σε ζευγάρι διπλών εισαγωγικών ("). Ένα αλφαριθμητικό μπορεί να περιέχει οποιουσδήποτε χαρακτήρες. Να σημειώσω πως αν χρησιμοποιήσετε ΜΟΝΟ αριθμούς (π.χ. το "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 Δείχνει ότι ο ΥΥΥ είναι οκταδικός αριθμός
--------------------------------------------------------------------------------
Όταν έχετε πολλά δεδομένα (π.χ. τιμές) που αφορούν στο ίδιο θέμα, τότε έχετε 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 όπως ακριβώς και στους μονοδιάστατους.