# Από Awk σε Perl Ξέρετε το `awk`. Σκέφτεστε με όρους `pattern { action }`, διαχωρίζετε κάθε γραμμή σε `$1`, `$2`, `$3` χωρίς να το ζητάτε, μετράτε πράγματα με `a[key]++`, και πλαισιώνετε όλη την εργασία με `BEGIN { }` και `END { }`. Η Perl γράφτηκε εν μέρει ως «awk, αλλά μεγαλύτερο» - σχεδόν ό,τι κάνετε στο awk έχει μια άμεση μορφή στην Perl, και ύστερα η Perl συνεχίζει: πραγματικές δομές δεδομένων, πλήρες regex, αρθρώματα, και μια γλώσσα που δεν μένει από χώρο όταν το σενάριο μεγαλώνει. Η μία ιδέα που πρέπει να κρατήσετε από την πρώτη γραμμή: **η Perl είναι σχεδόν υπερσύνολο του awk.** Ο βρόχος εγγραφών, τα πεδία, οι συσχετιστικοί πίνακες, οι δεσμευμένες λέξεις `BEGIN`/`END`, οι `print` και `printf`, οι `length`/`substr`/`split` - αντιστοιχίζονται όλα, συχνά με το *ίδιο όνομα*. Αυτό που αλλάζει είναι ότι τα πεδία γίνονται ένας πραγματικός πίνακας `@F` που μπορείτε να κάνετε `push` και `pop`, ο συσχετιστικός πίνακας γίνεται ένας κατακερματισμός της Perl που μπορείτε να εμφωλεύσετε, και η μηχανή regex είναι PCRE αντί για POSIX ERE. Αυτός ο οδηγός ξεκινά με τις ένα-προς-ένα αντιστοιχίσεις και επισημαίνει τα λίγα σημεία όπου η αναλογία διαρρέει. Every runnable example here was executed on `pperl`, a Rust reimplementation of Perl 5.42; the `# ...` comments show its real output. Start scripts with: ```perl use v5.42; ``` Αυτή η μία γραμμή ενεργοποιεί το `strict`, τα `warnings` και την ενσωματωμένη `say` (μια `print` με τελική νέα γραμμή), το σύγχρονο σύνολο χαρακτηριστικών που χρησιμοποιείται παντού. #### NOTE Διακόπτες one-liner σε αυτό το build του pperl Ο φυσικός δίδυμος του awk στην Perl είναι το one-liner: `perl -ne`, `perl -ane`, και `perl -F, -ane`. Αυτοί οι διακόπτες είναι **σωστή perl5 5.42** και εμφανίζονται σε όλον αυτόν τον οδηγό ως η ιδιωματική μορφή. Όμως η οικογένεια διακοπτών έμμεσου βρόχου (`-n`, `-p`, `-a`, `-F`, `-i`, `-l`) **δεν αναγνωρίζεται ακόμη από αυτό το build του `pperl`** - μόνο το `-e` αναγνωρίζεται. Έτσι τα παραδείγματα one-liner παρακάτω εμφανίζονται *χωρίς* έξοδο, και καθένα συνοδεύεται από μια μορφή πλήρους σεναρίου με ρητό βρόχο (`while (<$fh>) { ... }`) που **όντως** τρέχει στο `pperl` σήμερα και φέρει την επαληθευμένη έξοδο. Χρησιμοποιήστε τη μορφή πλήρους σεναρίου μέχρι να προστεθούν οι διακόπτες. ## Πώς να το διαβάσετε - [Ο βρόχος γραμμής/εγγραφής]() - το έμμεσο `{ ... }` του awk γίνεται `while (<$fh>)` και `$_`. - [Πεδία και διαχωρισμός]() - τα `$1`/`$NF`/`NF`/`FS` γίνονται `@F`, `$F[0]`, `split`, `-F`. - [Regex και αντικατάσταση]() - τα `gsub`/`sub`/ `match` γίνονται `s///`/`=~`, ένα γνήσιο υπερσύνολο. - [Διευθύνσεις και εύρη]() - τα `/re/`, `cond { }` και `/a/,/b/` γίνονται μεταθεματικό `if` και το flip-flop. - [I/O, σωληνώσεις, επιτόπια επεξεργασία]() - `getline`, ανακατεύθυνση και `-i`. - [One-liners και διακόπτες]() - `-n`, `-a`, `-F`, `BEGIN`/`END`. - [Ροή ελέγχου]() - `next`, `for`, οι μεταθεματικές μορφές. - [Μεταβλητές και δεδομένα]() - οι συσχετιστικοί πίνακες γίνονται κατακερματισμοί, η ευτυχής αντιστοίχιση. - [Παγίδες ερχόμενοι από το awk]() - ρίξτε μια ματιά πριν γράψετε. ## Ο βρόχος γραμμής/εγγραφής Το awk τρέχει το πρόγραμμά σας μία φορά ανά εγγραφή εισόδου, με την εγγραφή στο `$0` και τη δράση στο `{ ... }`. Το αντίστοιχο της Perl είναι ένας ρητός βρόχος με την εγγραφή στο `$_`, την έμμεση μεταβλητή θέματος. Ο διακόπτης `-n` αναπαράγει το «τρέξε αυτό το μπλοκ ανά γραμμή» του awk· το `NR` (αριθμός εγγραφής) του awk είναι το `$.` της Perl. ```awk awk '{ print }' input.txt ``` ```perl perl -ne 'print' input.txt ``` Η εκτελέσιμη μορφή πλήρους σεναρίου του ίδιου βρόχου - αυτή που τρέχει στο `pperl` σήμερα - ανοίγει το αρχείο και το διατρέχει ρητά. Το `$_` είναι η τρέχουσα εγγραφή, ακριβώς όπως το `$0` του awk: ```perl use v5.42; open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; while (<$fh>) { # each line lands in $_, like awk's $0 chomp; say "line $. : $_"; # $. is awk's NR } # line 1 : alice 85 90 # line 2 : bob 70 60 # line 3 : carol 95 100 # line 4 : alice 50 55 ``` (Το αρχείο εισόδου περιείχε `alice 85 90`, `bob 70 60`, `carol 95 100`, `alice 50 55`.) Η παγίδα: μια γραμμή που διαβάζεται με `<$fh>` κρατά την τελική νέα γραμμή της, σε αντίθεση με το `$0` του awk, από το οποίο έχει αφαιρεθεί ο διαχωριστής εγγραφών. Καλέστε `chomp` για να την αφαιρέσετε (το chomp είναι το πιο κοντινό πράγμα στο «το awk ήδη αφαίρεσε τη νέα γραμμή για σένα»). Ο αριθμός εγγραφής είναι `$.` = το `NR` του awk· όταν διαβάζετε πολλά αρχεία με `<>`, το `$.` συνεχίζει να μετρά σε όλα τους, οπότε ο μηδενισμός του `FNR` (μετρητής ανά αρχείο) του awk είναι κάτι που οργανώνετε εσείς ελέγχοντας το `eof`. ## Πεδία και διαχωρισμός Αυτή είναι η καρδιά του awk, και η Perl σάς δίνει την ίδια εικόνα με μία ανατροπή: τα πεδία ζουν σε έναν πραγματικό πίνακα `@F`, οπότε το `$1` του awk είναι το `$F[0]` της Perl (μηδενοβάσει), το `$0` είναι το `$_`, και το `NF` είναι το πλήθος `scalar @F`. Υπό τον διακόπτη `-a` η Perl αυτοδιαχωρίζει κάθε γραμμή σε `@F` ακριβώς όπως το awk αυτοδιαχωρίζει σε `$1..$NF`· το εκτελέσιμο αντίστοιχο είναι ένα ρητό `split`. ```awk awk '{ print $1, NF }' input.txt # first field, field count awk -F, '{ print $1 }' input.txt # comma separator (FS) ``` ```perl perl -ane 'print "$F[0] @{[scalar @F]}\n"' input.txt # -a fills @F perl -F, -ane 'print "$F[0]\n"' input.txt # -F sets the split pattern ``` Η μορφή πλήρους σεναρίου κάνει τον διαχωρισμό η ίδια. Το γυμνό `split` χωρίς ορίσματα διαχωρίζει το `$_` βάσει κενών χαρακτήρων και απορρίπτει τα αρχικά κενά - ακριβώς το προεπιλεγμένο `FS` του awk: ```perl use v5.42; open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; while (<$fh>) { my @F = split; # awk's automatic field split say "$F[0] has ", scalar(@F) - 1, " scores"; # $F[0]=$1, @F=NF } # alice has 2 scores # bob has 2 scores # carol has 2 scores # alice has 2 scores ``` Η αριθμητική κατά μήκος των πεδίων είναι η καθημερινή δράση `print $1, $2+$3` του awk, και διαβάζεται σχεδόν πανομοιότυπα: ```perl use v5.42; open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; while (<$fh>) { my @F = split; say "$F[0]: ", $F[1] + $F[2]; # awk: print $1, $2+$3 } # alice: 175 # bob: 130 # carol: 195 # alice: 105 ``` Το `$NF` (το τελευταίο πεδίο) του awk είναι ο αρνητικός δείκτης `$F[-1]` της Perl, και ένα μη προεπιλεγμένο `FS` είναι το όρισμα μοτίβου του `split`: ```perl use v5.42; open my $fh, '<', '/tmp/data.csv' or die "open: $!"; # alice,85,90 ... while (<$fh>) { chomp; my @F = split /,/; # awk -F, say $F[-1]; # awk's $NF: last field } # 90 # 60 # 100 ``` Για να ενώσετε ξανά τα πεδία με έναν διαχωριστή (το `OFS` του awk), ορίστε το `$,` (τον διαχωριστή πεδίων εξόδου που η `print`/`say` εισάγει ανάμεσα στα ορίσματα της λίστας της): ```perl use v5.42; open my $fh, '<', '/tmp/data.csv' or die "open: $!"; while (<$fh>) { chomp; my @F = split /,/; local $, = "|"; # OFS say @F; # join the list with OFS } # alice|85|90 # bob|70|60 # carol|95|100 ``` Η παγίδα: τα πεδία είναι **μηδενοβάσει**. Το `$1` του awk είναι `$F[0]`, το `$2` είναι `$F[1]`, και το `$0` (όλη η εγγραφή) είναι `$_`, όχι στοιχείο του `@F`. Το `NF` είναι `scalar @F` (ή `$#F + 1`)· το τελευταίο πεδίο είναι `$F[-1]`, όχι `$F[NF]`. Και το `split` με **ένα κενό ως συμβολοσειρά** (`split ' '`) είναι η ειδική λειτουργία κενών-και-περικοπής· το `split / /` (ένα regex με ένα κενό) είναι κυριολεκτικό και *δεν* περικόπτει τα αρχικά κενά. Το σκέτο `split` χωρίς όρισμα είναι η προεπιλεγμένη λειτουργία τύπου awk που θέλετε τις περισσότερες φορές. ## Regex και αντικατάσταση Τα `sub`, `gsub`, `match` και `~` του awk αντιστοιχίζονται όλα στη μία σύνταξη regex της Perl: το `=~` δένει ένα μοτίβο σε μια συμβολοσειρά, το `s///` αντικαθιστά, το `s///g` είναι καθολικό (το `gsub` έναντι του `sub` του awk). Η μηχανή της Perl είναι PCRE, ένα γνήσιο υπερσύνολο του POSIX ERE του awk, οπότε κάθε μοτίβο awk λειτουργεί και η Perl προσθέτει οπισθοαναφορές, lookaround, επώνυμες συλλήψεις και τις σημαίες `/e`, `/r`, `/x` για τις οποίες το awk δεν έχει αντίστοιχο. ```awk awk '/^alice/ { sub(/alice/, "ALICE"); print }' input.txt ``` ```perl perl -ne 'if (/^alice/) { s/alice/ALICE/; print }' input.txt ``` Η μορφή πλήρους σεναρίου: ```perl use v5.42; open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; while (<$fh>) { next unless /^alice/; # awk: /^alice/ { ... } s/alice/ALICE/; # awk: sub(/alice/, "ALICE") (first match) print; } # ALICE 85 90 # ALICE 50 55 ``` Χρησιμοποιήστε `s///g` για το `gsub` (κάθε αντιστοιχία) του awk, και οι ομάδες σύλληψης καταλήγουν στα `$1`, `$2` ακριβώς όπως το `match` του awk γεμίζει τα `RSTART`/`RLENGTH` συν τον πίνακα του `gawk`. Η σημαία `/e` εκτελεί **αυθαίρετη Perl** στην αντικατάσταση, κάτι που το awk δεν μπορεί να κάνει: ```perl use v5.42; my $s = "items: 3 and 4"; (my $t = $s) =~ s/(\d+)/$1 * 10/ge; # /e: replacement is Perl code say $t; # items: 30 and 40 ``` Οι `length`, `substr`, `index` και οι συναρτήσεις πεζών/κεφαλαίων αντιστοιχίζονται κατ” όνομα - προσέξτε την ευρετηρίαση: ```perl use v5.42; my $s = "alice"; say length $s; # 5 (awk length($s)) say substr $s, 0, 3; # ali (awk substr($s,1,3) - awk is 1-based!) say uc $s; # ALICE (awk toupper($s)) ``` Η παγίδα: η `substr` είναι **μηδενοβάσει** ενώ η `substr` του awk είναι βάσει του ένα, οπότε το `substr($s,1,3)` του awk είναι `substr($s,0,3)` στην Perl. Και το `s///` επεξεργάζεται τον στόχο του επιτόπια και επιστρέφει το **πλήθος** των αντικαταστάσεων, όχι τη νέα συμβολοσειρά - οπότε το `my $new = ($s =~ s/a/b/)` βάζει έναν αριθμό στο `$new`. Αντιγράψτε πρώτα (`(my $t = $s) =~ s/.../.../`) ή χρησιμοποιήστε τη σημαία `/r` για να πάρετε μια φρέσκια συμβολοσειρά. Οι κλάσεις χαρακτήρων POSIX (`[[:digit:]]`) λειτουργούν, αλλά οι συντομογραφίες της Perl (`\d`, `\w`, `\s`) είναι συντομότερες και αυτό που θα δείτε στον κώδικα Perl. ## Διευθύνσεις και εύρη Ένας κανόνας awk είναι `pattern { action }`: η δράση τρέχει μόνο σε εγγραφές που ταιριάζουν με το μοτίβο. Στην Perl αυτό το μοτίβο είναι απλώς μια συνθήκη στην εντολή - ένα μεταθεματικό `if` που ελέγχει το `$_`. Ένα γυμνό `/re/` ταιριάζει έναντι του `$_`, ένας αριθμητικός έλεγχος χρησιμοποιεί το `$.` (το `NR` του awk), και το εύρος `/start/,/end/` του awk είναι ο χειριστής **flip-flop** `/start/ .. /end/` της Perl. ```awk awk 'NR==2' input.txt # print record 2 awk '/banana/' input.txt # print matching records awk '/bob/,/carol/' input.txt # range, inclusive ``` ```perl perl -ne 'print if $. == 2' input.txt perl -ne 'print if /banana/' input.txt perl -ne 'print if /bob/ .. /carol/' input.txt ``` Το flip-flop πλήρους σεναρίου, αληθές από τη γραμμή που ταιριάζει με το αριστερό μοτίβο έως τη γραμμή που ταιριάζει με το δεξί, συμπεριλαμβανομένων - ακριβώς το εύρος δύο μοτίβων του awk: ```perl use v5.42; open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; while (<$fh>) { print if /bob/ .. /carol/; # awk: /bob/,/carol/ } # bob 70 60 # carol 95 100 ``` Η παγίδα: ένα γυμνό `pattern` χωρίς δράση στο awk σημαίνει «εκτύπωσε την εγγραφή»· στην Perl ο βρόχος είναι ρητός, οπότε γράφετε εσείς την `print` (ή `say`). Και σε έναν λογικό έλεγχο το `..` είναι ο χειριστής flip-flop (εύρους), **όχι** ο χειριστής εύρους λίστας (το `1 .. 10` χτίζει μια λίστα). Η Perl τα ξεχωρίζει βάσει περιβάλλοντος: στη βαθμωτή θέση ενός `if`, το `..` είναι ο επιλογέας εύρους γραμμών με κατάσταση που αντικατοπτρίζει το `addr1,addr2` του awk. ## I/O, σωληνώσεις, επιτόπια επεξεργασία Το awk διαβάζει αυτόματα κάθε αρχείο που ονομάζεται στη γραμμή εντολών του και έχει το `getline` για ρητές αναγνώσεις. Το `<>` (ο χειριστής διαμαντιού) της Perl διαβάζει με τη σειρά κάθε αρχείο στο `@ARGV` - η ίδια προεπιλογή - και το `<$fh>` είναι το ρητό `getline`. Η εγγραφή σε μια εντολή ή η ανάγνωση από μία (τα `print | "cmd"` και `"cmd" | getline` του awk) είναι ένας περιγραφέας σωλήνωσης. ```awk awk '{ print > "out.txt" }' input.txt # redirect output awk '{ print | "sort" }' input.txt # pipe to a command ``` ```perl use v5.42; open my $out, '>', '/tmp/out.txt' or die "write: $!"; open my $in, '<', '/tmp/scores.txt' or die "read: $!"; while (<$in>) { print {$out} $_; # awk: print > "out.txt" } close $out; ``` Για σωληνώσεις, το `open my $fh, '|-', 'sort'` γράφει στην είσοδο μιας εντολής και το `open my $fh, '-|', 'ls', '-l'` διαβάζει την έξοδο μιας εντολής - το `| "cmd"` του awk και προς τις δύο κατευθύνσεις. Το awk δεν έχει ενσωματωμένη επιτόπια επεξεργασία· καταφεύγετε σε ανακάτεμα `tmp`-αρχείου ή στο GNU `gawk -i inplace`. Η Perl το γράφει `-i` σε ένα one-liner: ```perl perl -i -pe 's/red/crimson/' file.txt # edit in place ``` Το εκτελέσιμο αντίστοιχο σήμερα είναι μια ρητή ανάγνωση-τροποποίηση-εγγραφή - ρουφήξτε τις γραμμές, μετασχηματίστε, γράψτε πίσω - που είναι ακριβώς αυτό που κάνει το `-i` στο παρασκήνιο: ```perl use v5.42; my $path = '/tmp/inplace.txt'; # holds: red green blue open my $in, '<', $path or die "read: $!"; my @lines = <$in>; # slurp all lines (list context) close $in; s/e/E/g for @lines; # transform each line's $_ open my $out, '>', $path or die "write: $!"; print {$out} @lines; # write them back close $out; # file now holds: rEd / grEEn / bluE ``` Η παγίδα: η `open` επιστρέφει ψευδές σε αποτυχία αντί να ματαιώσει, οπότε το ιδίωμα `or die "...: $!"` είναι υποχρεωτικό - το `$!` είναι η συμβολοσειρά σφάλματος του συστήματος, η σιωπηλή αποτυχία του awk γινόμενη ρητή. Και το `print {$out} ...` χρειάζεται τα άγκιστρα γύρω από τον περιγραφέα όταν είναι μεταβλητή· η γυμνή μορφή `print FH ...` (χωρίς κόμμα) είναι για επώνυμους περιγραφείς, κάτι που αποτελεί συχνό σκόνταμμα της πρώτης μέρας. ## One-liners και διακόπτες Το one-liner είναι το οικείο έδαφος του awk, και της Perl επίσης. Η οικογένεια διακοπτών που αναπαράγει το awk: το `-e` παρέχει το πρόγραμμα, το `-n` το τυλίγει σε έναν βρόχο ανά εγγραφή (το έμμεσο `{ }` του awk), το `-p` κάνει το ίδιο με μια αυτόματη εκτύπωση στο τέλος κάθε κύκλου, το `-a` αυτοδιαχωρίζει σε `@F` (ο αυτόματος διαχωρισμός πεδίων του awk), το `-F` ορίζει το μοτίβο διαχωρισμού (το `FS` του awk), και το `-l` χειρίζεται τα τέλη γραμμών (η ευκολία `ORS`/`RS` του awk). Τα `BEGIN { }` και `END { }` είναι οι **ίδιες δεσμευμένες λέξεις** με το awk, τρέχοντας πριν και μετά τον βρόχο. ```awk awk '/error/ { print }' log.txt # print error records awk '{ s += $NF } END { print s }' input.txt # sum the last field awk -F, '{ print $1 }' input.txt # first CSV field ``` ```perl perl -ne 'print if /error/' log.txt perl -ane 'END { print "$s\n" } $s += $F[-1]' input.txt perl -F, -ane 'print "$F[0]\n"' input.txt ``` Αυτά εμφανίζονται χωρίς έξοδο επειδή οι διακόπτες έμμεσου βρόχου δεν αναγνωρίζονται ακόμη από αυτό το build του `pperl` (δείτε τη σημείωση στην αρχή). Καθένας αντιστοιχίζεται σε μια επαληθευμένη μορφή ρητού βρόχου: το `perl -ne 'CODE'` είναι `while (<$fh>) { CODE }`, το `-ane` προσθέτει `my @F = split` στην αρχή, και τα μπλοκ `BEGIN`/`END` λειτουργούν σε ένα πλήρες σενάριο ακριβώς όπως στο awk. Η εκτελέσιμη μορφή «άθροισε μια στήλη με BEGIN/END»: ```perl use v5.42; my $n = 0; my $sum = 0; BEGIN { say "start" } # runs before the loop, like awk BEGIN END { say "lines=$n sum=$sum" } # runs after, like awk END open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; while (<$fh>) { my @F = split; $n++; $sum += $F[1]; # awk: sum += $2 } # start # lines=4 sum=300 ``` Η παγίδα: μέχρι να προστεθούν οι διακόπτες στο `pperl`, γράψτε τον ρητό βρόχο (τρέχει σήμερα και είναι ούτως ή άλλως αυτό στο οποίο αναπτύσσονται οι διακόπτες), και συμβουλευτείτε τον [οδηγό One-Liners](../oneliner/index.md) για τον πλήρη κατάλογο διακοπτών και δεκάδες συνταγές - είναι το πιο αποδοτικό πράγμα που μπορείτε να διαβάσετε ερχόμενοι από υπόβαθρο awk. Η Perl έχει επίσης πλήρεις υπορουτίνες, αναφορές, αρθρώματα και OO αν το σενάριό σας ξεπεράσει ένα one-liner· δείτε τον [οδηγό Αντικειμενοστραφούς Προγραμματισμού](../oop/index.md) και τους άλλους οδηγούς coming-from. ## Ροή ελέγχου Οι δεσμευμένες λέξεις ελέγχου του awk αντιστοιχούν στις αντίστοιχες της Perl σχεδόν λέξη προς λέξη. Το `next` του awk (παράλειψη προς την επόμενη εγγραφή) είναι το `next` της Perl· το `break`/`continue` στους βρόχους του awk είναι το `last`/`next` της Perl· το `for (k in arr)` του awk γίνεται ένα `for` της Perl πάνω στα `keys`. Οι αγκύλες και το `;` είναι ίδια· η Perl προσθέτει τις μεταθεματικές μορφές που διαβάζονται σαν μοτίβα του awk. ```awk awk '{ if ($2 < 70) next; print }' input.txt ``` ```perl use v5.42; open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; while (<$fh>) { my @F = split; next if $F[1] < 70; # awk: if ($2 < 70) next print; } # alice 85 90 # carol 95 100 ``` Η παγίδα: οι χειριστές σύγκρισης της Perl έρχονται σε δύο οικογένειες και επιλέγετε βάσει του *τύπου των τελεστέων*, ενώ το awk έχει μόνο `<`/`==` και για τους δύο. Οι αριθμοί χρησιμοποιούν `==`, `<`, `>`, `<=>`· οι συμβολοσειρές χρησιμοποιούν `eq`, `lt`, `gt`, `cmp`. Το awk αποφασίζει αριθμητική-έναντι-συμβολοσειρά σύγκριση βάσει των τιμών κατά τον χρόνο εκτέλεσης· η Perl σάς κάνει να επιλέξετε τον χειριστή. Η χρήση του `==` σε συμβολοσειρές (ή του `eq` σε αριθμούς) είναι ένα συχνό, σιωπηλό σφάλμα - το `"abc" == "xyz"` είναι αληθές στην Perl (και τα δύο μετατρέπονται αριθμητικά σε 0), κάτι που σχεδόν ποτέ δεν είναι αυτό που εννοούσατε. ## Μεταβλητές και δεδομένα Αυτή είναι η πιο ευτυχής αντιστοίχιση στον οδηγό: ο συσχετιστικός πίνακας του awk είναι ένας **κατακερματισμός** της Perl, και τα καθημερινά ιδιώματα είναι σχεδόν πανομοιότυπα. Το `count[$1]++` του awk είναι το `$count{$F[0]}++` της Perl· το `for (k in count)` είναι `for my $k (keys %count)`· το `if (k in count)` είναι `exists $count{$k}`. Η μόνη μετατόπιση είναι το sigil - ολόκληρος ο κατακερματισμός είναι `%count`, αλλά μία τιμή είναι `$count{$key}`. ```awk awk '{ total[$1] += $2 + $3 } END { for (k in total) print k, total[k] }' input.txt ``` ```perl use v5.42; open my $fh, '<', '/tmp/scores.txt' or die "open: $!"; my %total; while (<$fh>) { my @F = split; $total{$F[0]} += $F[1] + $F[2]; # awk: total[$1] += $2 + $3 } for my $name (sort keys %total) { # awk END loop over the array say "$name $total{$name}"; } # alice 280 # bob 130 # carol 195 ``` Το ιδίωμα δύο αρχείων `NR==FNR` - φόρτωσε κλειδιά από το πρώτο αρχείο, μετά έλεγξε την ιδιότητα μέλους στο δεύτερο - είναι ένας ρητός βρόχος ανά αρχείο στην Perl, που είναι σαφέστερος από το one-liner του awk που αντικαθιστά: ```perl use v5.42; my %seen; open my $f1, '<', '/tmp/data.csv' or die "open: $!"; while (<$f1>) { my ($k) = split /,/; # first field only $seen{$k} = 1; } close $f1; open my $f2, '<', '/tmp/scores.txt' or die "open: $!"; while (<$f2>) { my ($k) = split; print if $seen{$k}; # awk: ($1 in seen) } # alice 85 90 # bob 70 60 # carol 95 100 # alice 50 55 ``` Η παγίδα: ένας κατακερματισμός δεν έχει εγγενή σειρά (το `for (k in arr)` του awk είναι επίσης χωρίς σειρά, οπότε αυτό δεν θα σας εκπλήξει), οπότε επαναλαμβάνετε με `sort keys %h` όταν θέλετε σταθερή σειρά. Η ιδιότητα μέλους είναι `exists $h{$k}`, όχι το `k in arr` του awk - και απλώς το *να διαβάσετε* το `$h{$k}` για να το ελέγξετε θα αυτο-δημιουργήσει το κλειδί σε ύπαρξη σε κάποια περιβάλλοντα, ενώ το `exists` ποτέ δεν το κάνει. Οι αριθμοί και οι συμβολοσειρές αυτο-εξαναγκάζονται στην Perl όπως στο awk, αλλά θυμηθείτε τον διαχωρισμό `==` έναντι `eq` από τη [Ροή ελέγχου](). ## Παγίδες ερχόμενοι από το awk Οι παγίδες, συγκεντρωμένες για μια τελική ματιά πριν γράψετε: - **Τα πεδία είναι μηδενοβάσει.** Το `$1` του awk είναι `$F[0]`, το `$2` είναι `$F[1]`, και όλη η εγγραφή `$0` είναι `$_`. Το `NF` είναι `scalar @F`· το τελευταίο πεδίο είναι `$F[-1]`, όχι `$F[NF]`. - **Το `<$fh>` κρατά την τελική νέα γραμμή,** σε αντίθεση με το `$0` του awk. Κάντε `chomp` για να την αφαιρέσετε. - **Η `substr` είναι μηδενοβάσει,** ενώ του awk είναι βάσει του ένα: το `substr($s,1,3)` του awk είναι `substr($s,0,3)` στην Perl. - **Το `s///` επιστρέφει ένα πλήθος, όχι τη νέα συμβολοσειρά.** Το `my $x = $s =~ s/a/b/` βάζει το *πλήθος* αντικαταστάσεων στο `$x`. Αντιγράψτε πρώτα, ή χρησιμοποιήστε τη σημαία `/r` για να πάρετε μια νέα συμβολοσειρά. - **Δύο οικογένειες σύγκρισης.** Οι αριθμοί χρησιμοποιούν `==`/`<`/`<=>`· οι συμβολοσειρές χρησιμοποιούν `eq`/`lt`/`cmp`. Το μοναδικό `==`/`<` του awk χωρίζεται στα δύο, επιλεγόμενο βάσει χειριστή, όχι βάσει τιμής. Το `"abc" == "xyz"` είναι αληθές (και τα δύο μετατρέπονται αριθμητικά σε 0). - **Το γυμνό `split` είναι η προεπιλεγμένη λειτουργία τύπου awk.** Το `split` (χωρίς ορίσματα) διαχωρίζει το `$_` βάσει κενών χαρακτήρων και αφαιρεί τα αρχικά κενά. Το `split / /` (ένα regex με ένα κενό) είναι κυριολεκτικό και *δεν* αφαιρεί - χρησιμοποιήστε το σκέτο `split` για το προεπιλεγμένο `FS` του awk. - **Η ιδιότητα μέλους είναι `exists $h{$k}`, όχι `k in arr`.** Ο έλεγχος ενός κλειδιού μέσω ανάγνωσης του `$h{$k}` μπορεί να το αυτο-δημιουργήσει· το `exists` ποτέ δεν το κάνει. - **Η `open` δεν ματαιώνει σε αποτυχία.** Χρησιμοποιήστε `open my $fh, '<', $path or die "...: $!"`. Το `$!` είναι η συμβολοσειρά σφάλματος του συστήματος. - **Οι διακόπτες έμμεσου βρόχου (`-n`/`-p`/`-a`/`-F`/`-i`/`-l`) δεν αναγνωρίζονται ακόμη από αυτό το build του `pperl`.** Μόνο το `-e` λειτουργεί σήμερα. Γράψτε τον ρητό βρόχο `while (<$fh>) { my @F = split; ... }`, που είναι ακριβώς αυτό στο οποίο αναπτύσσονται οι διακόπτες, μέχρι να προστεθούν.