Από Shell σε Perl#
Ξέρετε το κέλυφος. Κολλάτε προγράμματα μεταξύ τους με σωληνώσεις, καταφεύγετε στα sed, awk, grep και cut σε μια σωλήνωση χωρίς σκέψη, συλλαμβάνετε την έξοδο εντολών με $(...), και προγραμματίζετε ροή ελέγχου με for f in *, while read line και case. Αυτός ο οδηγός αντιστοιχίζει καθεμία από αυτές τις συνήθειες στην Perl, ώστε να κρατήσετε τα αντανακλαστικά σωλήνωσης και να αποκτήσετε τα πράγματα που το κέλυφος κάνει επώδυνα.
Όταν ένα σενάριο κελύφους μεγαλώσει πέρα από περίπου 50 γραμμές παράθεσης σε εισαγωγικά και σωληνώσεων sed/awk/grep, η Perl είναι το φυσικό επόμενο βήμα: τα κάνει όλα σε μία γλώσσα, με πραγματικές δομές δεδομένων αντί για συμβολοσειρές οριοθετημένες με κενά. Το πλαίσιο που πρέπει να κρατήσετε: η Perl είναι η σωλήνωση κελύφους, αλλά η γλώσσα του φίλτρου είναι μια πραγματική γλώσσα. Εξακολουθείτε να κάνετε open σε σωληνώσεις, εξακολουθείτε να κάνετε glob, εξακολουθείτε να τρέχετε εντολές και να διαβάζετε την έξοδό τους. Αυτό που αλλάζει είναι ότι ανάμεσα στις σωληνώσεις έχετε τώρα @arrays, %hashes, πραγματικό regex, και αριθμητική που δεν χρειάζεται $(( )). Ο ένας τύπος δεδομένων του κελύφους είναι η συμβολοσειρά· η Perl σάς δίνει πραγματικούς περιέκτες.
Every runnable example here was executed on pperl, a Rust reimplementation of Perl 5.42; the # ... comments show its real output. Start scripts with:
use v5.42;
Αυτή η μία γραμμή ενεργοποιεί το strict, τα warnings και την ενσωματωμένη say (μια print με τελική νέα γραμμή), το σύγχρονο σύνολο χαρακτηριστικών που χρησιμοποιείται παντού.
Σημείωση
Διακόπτες one-liner σε αυτό το build του pperl Ο φυσικός δίδυμος του προγραμματιστή κελύφους στην Perl είναι το one-liner: perl -ne και perl -pe, το φίλτρο μέσα στη σωλήνωση. Αυτοί οι διακόπτες είναι σωστή perl5 5.42 και εμφανίζονται σε όλον αυτόν τον οδηγό ως η ιδιωματική μορφή. Όμως η οικογένεια διακοπτών έμμεσου βρόχου (-n, -p, -a, -F, -i, -l) δεν αναγνωρίζεται ακόμη από αυτό το build του pperl - μόνο το -e αναγνωρίζεται. Έτσι τα παραδείγματα one-liner παρακάτω εμφανίζονται χωρίς έξοδο, και καθένα συνοδεύεται από μια μορφή πλήρους σεναρίου με ρητό βρόχο (while (<$fh>) { ... }) που όντως τρέχει στο pperl σήμερα και φέρει την επαληθευμένη έξοδο. Χρησιμοποιήστε τη μορφή πλήρους σεναρίου μέχρι να προστεθούν οι διακόπτες.
Πώς να το διαβάσετε#
Ο βρόχος εγγραφών - το
while read lineγίνεταιwhile (<$fh>)και$_.Πεδία και διαχωρισμός - τα πεδία
cut/IFS/awkγίνονταιsplitκαι ένας πραγματικός πίνακας.Regex και αντικατάσταση - οι παραμετρικές αναπτύξεις
${var#...}/${var/a/b}του κελύφους γίνονται πραγματικό regex.Διευθύνσεις και εύρη -
grep -n,awk NR==2και εύρη γραμμών.I/O, σωληνώσεις και επιτόπια επεξεργασία - η καρδιά:
open,-|/|-, ανακατεύθυνση, here-docs,$(...).One-liners και διακόπτες -
-n,-p,-e,BEGIN/END.Ροή ελέγχου -
for/while/case/if, έλεγχοι αρχείων, κατάσταση εξόδου.Μεταβλητές και δεδομένα - το
$varγίνεταιmy $var, συν τους@arraysκαι%hashesπου το κέλυφος ποτέ δεν είχε.Παγίδες ερχόμενοι από το κέλυφος - ρίξτε μια ματιά πριν γράψετε.
Ο βρόχος εγγραφών#
Το ιδίωμα γραμμή-τη-φορά του κελύφους είναι while read line; do ... done, που διαβάζει μία γραμμή ανά επανάληψη στο $line (και την περικόπτει σύμφωνα με το IFS). Το αντίστοιχο της Perl είναι while (<$fh>), που διαβάζει μία γραμμή ανά επανάληψη στο $_, την έμμεση μεταβλητή θέματος. Η κρίσιμη διαφορά: η Perl κρατά την τελική νέα γραμμή, οπότε κάνετε εσείς chomp, ακριβώς εκεί όπου το read του κελύφους θα την είχε καταπιεί.
while read line; do
echo "got: $line"
done < data.txt
use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (my $line = <$fh>) { # each line, with its newline
chomp $line; # strip it, like read does
say "got: $line";
}
close $fh;
# got: apple
# got: banana
# got: cherry
(Το αρχείο εισόδου περιείχε apple, banana, cherry.) Διαβάστε με το γυμνό <$fh> και η γραμμή καταλήγει στο $_, την προεπιλογή στην οποία λειτουργούν οι περισσότερες ενσωματωμένες συναρτήσεις γραμμών. Ο αριθμός γραμμής βρίσκεται στο $.. Αυτή είναι η μορφή στην οποία καταφεύγετε όταν μεταφράζετε ένα φίλτρο:
use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (<$fh>) { # line lands in $_
chomp; # chomp $_ by default
say "$. : $_"; # $. is the line number
}
close $fh;
# 1 : apple
# 2 : banana
# 3 : cherry
Η παγίδα: το read διαχωρίζει βάσει του IFS και περικόπτει· το <$fh> δεν κάνει κανένα από τα δύο. Μια ανάγνωση γραμμής στην Perl είναι όλη η ακατέργαστη γραμμή συμπεριλαμβανομένου του \n, οπότε το chomp είναι το αντανακλαστικό σας, και ο διαχωρισμός πεδίων είναι ένα ξεχωριστό, ρητό βήμα (η επόμενη ενότητα). Ό,τι χάνετε στην αυτόματη περικοπή το κερδίζετε στο να μην παραμορφώνει σιωπηλά το IFS τα δεδομένα σας.
Πεδία και διαχωρισμός#
Στο κέλυφος φτάνετε στα πεδία με cut -d: -f1, με awk '{print $1}', ή ορίζοντας το IFS και αφήνοντας το read να διαχωρίσει για εσάς. Η μία απάντηση της Perl είναι το split: δώστε του έναν διαχωριστή και μια συμβολοσειρά, πάρτε πίσω έναν πραγματικό πίνακα. Τα θεσιακά πεδία που το κέλυφος αριθμεί $1, $2 γίνονται στοιχεία πίνακα $f[0], $f[1] - μηδενοβάσει, σε αντίθεση με το βάσει-του-ένα $1 του awk.
line='root:x:0:0:root:/root:/bin/bash'
user=$(echo "$line" | cut -d: -f1)
shell=$(echo "$line" | cut -d: -f7)
echo "$user $shell"
use v5.42;
my $line = "root:x:0:0:root:/root:/bin/bash";
my @f = split /:/, $line;
say "user: $f[0]"; # user: root
say "shell: $f[-1]"; # shell: /bin/bash (negative index)
say "fields: ", scalar @f; # fields: 7
Για να διαχωρίσετε σε διαδοχές κενών χαρακτήρων - τον προεπιλεγμένο διαχωρισμό λέξεων του κελύφους, και την προεπιλογή του awk - χρησιμοποιήστε το ειδικό μοτίβο split ' ' (μια κυριολεκτική συμβολοσειρά ενός κενού, που επίσης παραλείπει τα αρχικά κενά). Για να ενώσετε ξανά τα πεδία, join:
use v5.42;
my @words = split ' ', " the quick brown ";
say scalar @words; # 3 (leading/inner blanks collapsed)
say join "|", @words; # the|quick|brown
Η παγίδα: το split /:/ με ένα κυριολεκτικό μοτίβο κρατά κενά τελικά πεδία μόνο μέχρι ένα σημείο - εξ ορισμού η Perl απορρίπτει τα κενά τελικά πεδία, οπότε το split /:/, "a::" δίνει ("a"), όχι ("a", "", ""). Δώστε ένα αρνητικό όριο (split /:/, $s, -1) για να τα κρατήσετε, το αντίστοιχο του να ενδιαφέρεστε για τα κενά τελικά πεδία του cut. Και το split ' ' (η μαγική μορφή με κενά) δεν είναι το ίδιο με το split / / (ένα μοναδικό κυριολεκτικό κενό): η πρώτη συμπτύσσει διαδοχές και περικόπτει, η δεύτερη όχι.
Regex και αντικατάσταση#
Οι παραμετρικές αναπτύξεις ${} του κελύφους γίνονται πραγματικό regex - και αυτό είναι μια μεγάλη αναβάθμιση. Τα ${var#prefix}, ${var%suffix}, ${var/a/b} και ${var//a/b} είναι το καθένα μια ειδικού σκοπού, περιορισμένη επεξεργασία συμβολοσειράς. Η Perl αντικαθιστά ολόκληρη την οικογένεια με έναν χειριστή, το s///, που δέχεται μια πραγματική κανονική έκφραση και το πλήρες σύνολο σημαιών.
f='report.txt.bak'
base=${f%.bak} # strip suffix
path='/usr/local/bin'
tail=${path##*/} # basename
dir=${path%/*} # dirname
s='a-b-c'
one=${s/-/_} # replace first
all=${s//-/_} # replace all
use v5.42;
my $f = "report.txt.bak";
(my $base = $f) =~ s/\.bak$//; # ${f%.bak}
say $base; # report.txt
my $path = "/usr/local/bin";
(my $tail = $path) =~ s{.*/}{}; # ${path##*/} basename
say $tail; # bin
(my $dir = $path) =~ s{/[^/]*$}{}; # ${path%/*} dirname
say $dir; # /usr/local
my $s = "a-b-c";
(my $one = $s) =~ s/-/_/; # ${s/-/_} first only
say $one; # a_b-c
(my $all = $s) =~ s/-/_/g; # ${s//-/_} /g = global
say $all; # a_b_c
Επειδή είναι ένα πραγματικό regex, τα πράγματα που το κέλυφος δεν μπορεί να κάνει καθόλου είναι τετριμμένα: οι ομάδες σύλληψης καταλήγουν στα $1, $2· η σημαία /e εκτελεί κώδικα Perl στην αντικατάσταση· η σημαία /r επιστρέφει ένα αντίγραφο αντί για επιτόπια επεξεργασία· και τα /i, /x τροποποιούν το ταίριασμα.
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
my $date = "2026-06-21";
my $us = $date =~ s{(\d+)-(\d+)-(\d+)}{$2/$3/$1}r; # /r: return a copy
say $us; # 06/21/2026
say $date; # 2026-06-21 (unchanged)
Η παγίδα: το γυμνό s/// επεξεργάζεται τον στόχο του και επιστρέφει το πλήθος των αντικαταστάσεων, όχι τη νέα συμβολοσειρά - οπότε τα παραδείγματα αντιγράφουν πρώτα με (my $t = $s) =~ ... ή χρησιμοποιούν τη σημαία /r. Γράφοντας my $new = ($s =~ s/a/b/) βάζετε έναν αριθμό στο $new. Αυτή είναι η πιο συχνή έκπληξη της πρώτης μέρας. Σημειώστε επίσης ότι οι οριοθέτες είναι ελεύθεροι: τα s{...}{...} και s#...#...# σας γλιτώνουν από τις ανάποδες καθέτους στις καθέτους ενός μοτίβου διαδρομής, όπως το ${path##*/} του κελύφους ήδη τις αποφεύγει.
Διευθύνσεις και εύρη#
Η επιλογή γραμμών βάσει αριθμού ή μοτίβου - grep, grep -n, awk 'NR==2', sed -n '/a/,/b/p' - γίνεται ένα μεταθεματικό if σε μια συνθήκη μέσα στον βρόχο. Ο αριθμός γραμμής είναι $.· ο έλεγχος μοτίβου είναι /pat/ έναντι του $_.
grep -n an data.txt # number + matching lines
awk 'NR==2' data.txt # the second line
use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (<$fh>) {
chomp;
say "$. : $_" if /an/; # grep -n 'an'
}
close $fh;
# 2 : banana
Ένα εύρος γραμμών - ο επιλογέας /start/,/end/ του sed/awk - γίνεται ο χειριστής εύρους flip-flop της Perl /start/ .. /end/, αληθής από τη γραμμή που ταιριάζει με το αριστερό μοτίβο έως τη γραμμή που ταιριάζει με το δεξί, συμπεριλαμβανομένων:
use v5.42;
open my $fh, '<', '/tmp/data.txt' or die "open: $!";
while (<$fh>) {
print if /banana/ .. /cherry/; # the /start/,/end/ range
}
close $fh;
# banana
# cherry
Η παγίδα: σε έναν λογικό έλεγχο το .. είναι ο χειριστής flip-flop (εύρους), όχι ο χειριστής εύρους λίστας (που είναι το 1 .. 10 που χτίζει μια λίστα). Η Perl τα ξεχωρίζει βάσει περιβάλλοντος - στη λογική θέση ενός if, το .. είναι ο επιλογέας εύρους γραμμών με κατάσταση· σε περιβάλλον λίστας χτίζει μια ακολουθία. Οι ίδιες δύο τελείες, που αποφασίζονται από το πού τις γράφετε.
I/O, σωληνώσεις και επιτόπια επεξεργασία#
Αυτή είναι η καρδιά του οδηγού, επειδή είναι η καρδιά του προγραμματισμού κελύφους. Ό,τι κάνετε με |, >, >>, 2>&1, <<EOF και $(...) έχει μια άμεση γραφή στην Perl, και μόλις ένα σενάριο ξεπεράσει μια μοναδική σωλήνωση αυτή είναι η ενότητα που αποδίδει.
Υποκατάσταση εντολής. Τα $(cmd) και τα backticks του κελύφους γίνονται backticks της Perl ή qx{...}. Η σύλληψη εξαρτάται από το περιβάλλον: σε βαθμωτό περιβάλλον παίρνετε όλη την έξοδο ως μία συμβολοσειρά· σε περιβάλλον λίστας παίρνετε ένα στοιχείο ανά γραμμή.
out=$(echo hello)
mapfile -t lines < <(seq 1 3)
use v5.42;
my $out = `echo hello`; # scalar: whole output, one string
chomp $out;
say "got: $out"; # got: hello
my @lines = qx{seq 1 3}; # list: one element per line
chomp @lines;
say "lines: @lines"; # lines: 1 2 3
Όταν δεν χρειάζεστε την έξοδο, η system() τρέχει μια εντολή και σας δίνει την κατάσταση εξόδου της (δείτε τη Ροή ελέγχου για το $?). Δώστε στη system μια λίστα και δεν εμπλέκεται κανένα κέλυφος - η ασφαλής μορφή που αποφεύγει την κόλαση της παράθεσης σε εισαγωγικά:
use v5.42;
system('mkdir', '-p', '/tmp/sh_demo'); # list form: no shell, no quoting
say "made it" if -d '/tmp/sh_demo'; # made it
Σωληνώσεις. Ένα στάδιο σωλήνωσης - η ανάγνωση της εξόδου μιας εντολής ή η τροφοδοσία της εισόδου μιας εντολής - είναι ένα open με λειτουργία σωλήνωσης. Το open my $fh, '-|', LIST διαβάζει το stdout μιας εντολής· το open my $fh, '|-', LIST γράφει στο stdin μιας εντολής. Η μορφή λίστας (εντολή συν ορίσματα ως ξεχωριστές συμβολοσειρές) δεν τρέχει κανένα κέλυφος, οπότε δεν υπάρχει τίποτα προς παράθεση σε εισαγωγικά.
seq 1 3 | while read n; do echo "n=$n"; done # read from a command
printf '%s\n' banana apple cherry | sort # write to a command
use v5.42;
open my $ph, '-|', 'seq', '1', '3' or die "pipe: $!"; # read command output
my @nums = <$ph>;
close $ph;
chomp @nums;
say "got: @nums"; # got: 1 2 3
use v5.42;
open my $sort, '|-', 'sort' or die "pipe: $!"; # write to command stdin
print {$sort} "$_\n" for qw(banana apple cherry);
close $sort;
# apple
# banana
# cherry
Ανακατεύθυνση. Τα > και >> είναι οι λειτουργίες open '>' (περικοπή) και '>>' (προσθήκη)· ένα open τριών ορισμάτων με έναν λεξιλογικό περιγραφέα είναι το ιδίωμα. Το print {$fh} ... γράφει σε έναν περιγραφέα. Για να συγχωνεύσετε το stderr ενός παιδιού στο συλληφθέν ρεύμα (2>&1), κρατήστε την ανακατεύθυνση στην εντολή που δίνετε στα backticks:
use v5.42;
open my $fh, '>', '/tmp/sh_out.txt' or die "write: $!";
print {$fh} "line $_\n" for 1 .. 3;
close $fh;
my $both = `sh -c 'echo to-out; echo to-err 1>&2' 2>&1`; # merge stderr
print $both;
# to-out
# to-err
Here-docs. Τα <<EOF (με παρεμβολή) και <<'EOF' (κυριολεκτικά) του κελύφους αντιστοιχίζονται ένα-προς-ένα στα <<"END" και <<'END' της Perl. Τα διπλά εισαγωγικά στον τερματιστή παρεμβάλλουν μεταβλητές· τα μονά εισαγωγικά όχι.
use v5.42;
my $name = "world";
print <<"END"; # interpolating, like <<EOF
hello $name
END
print <<'END'; # literal, like <<'EOF'
no $interpolation here
END
# hello world
# no $interpolation here
Επιτόπια επεξεργασία. Το sed -i ξαναγράφει ένα αρχείο επιτόπια· η ιδιωματική Perl είναι ο ίδιος διακόπτης σε ένα one-liner -p. Το εκτελέσιμο αντίστοιχο σήμερα είναι η ρητή ανάγνωση-τροποποίηση-εγγραφή που εκτελεί το -i στο παρασκήνιο - ρούφηγμα, μετασχηματισμός, εγγραφή πίσω:
use v5.42;
my $path = '/tmp/inplace_data.txt'; # holds: red green blue
open my $in, '<', $path or die "read: $!";
my @lines = <$in>; # slurp all lines
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 "...: $!" είναι υποχρεωτικό - το $! είναι η συμβολοσειρά σφάλματος του συστήματος, η σιωπηλή αποτυχία 2>/dev/null του κελύφους γινόμενη ηχηρή και ρητή. Και όταν χτίζετε μια εντολή από μεταβλητές, προτιμήστε τη μορφή λίστας των system/open ('ls', '-l', $dir) έναντι μιας μοναδικής συμβολοσειράς με παρεμβολή: η μορφή λίστας παρακάμπτει εντελώς το κέλυφος, οπότε δεν υπάρχει διαχωρισμός λέξεων, ούτε ανάπτυξη glob, ούτε παράθεση σε εισαγωγικά να την κάνετε λάθος - η θεραπεία για την κόλαση παράθεσης του κελύφους.
One-liners και διακόπτες#
Το one-liner είναι το οικείο έδαφος του προγραμματιστή κελύφους, και της Perl επίσης: το φίλτρο που ρίχνετε σε μια σωλήνωση. Η οικογένεια διακοπτών που το κάνει να λειτουργεί: το -e παρέχει το πρόγραμμα, το -n το τυλίγει σε έναν βρόχο χωρίς αυτόματη εκτύπωση, το -p το τυλίγει σε έναν βρόχο με αυτόματη εκτύπωση, το -i επεξεργάζεται αρχεία επιτόπια, το -l χειρίζεται τα τέλη γραμμών, και τα -a/-F αυτοδιαχωρίζουν κάθε γραμμή σε @F (το άμεσο αντίστοιχο του awk, πεδία στο @F αντί για $1..$NF). Τα BEGIN { } και END { } τρέχουν πριν και μετά τον βρόχο, όπως τα BEGIN/END του awk.
grep error log.txt # filter lines
awk -F: '{print $1}' /etc/passwd # first field
awk '{s += $NF} END {print s}' data.txt # sum last field
perl -ne 'print if /error/' log.txt # filter lines
perl -F: -ane 'print "$F[0]\n"' /etc/passwd # first field, -F sets split
perl -lane '$s += $F[-1]; END { say $s }' data.txt # sum last field
Αυτά εμφανίζονται χωρίς έξοδο επειδή οι διακόπτες έμμεσου βρόχου δεν αναγνωρίζονται ακόμη από αυτό το build του pperl (δείτε τη σημείωση στην αρχή). Καθένας αντιστοιχίζεται σε μια επαληθευμένη μορφή ρητού βρόχου: το perl -ne 'CODE' είναι while (<$fh>) { CODE }, και το perl -pe 'CODE' είναι το ίδιο με μια τελική print. Η εκτελέσιμη μορφή «πρώτο πεδίο κάθε γραμμής»:
use v5.42;
open my $fh, '<', '/tmp/passwd.txt' or die "open: $!";
while (<$fh>) { # the implicit loop of -ane
chomp;
my @F = split /:/; # what -F: gives you in @F
say $F[0]; # print the first field
}
close $fh;
# root
# daemon
# bin
Η παγίδα: μέχρι να προστεθούν οι διακόπτες στο pperl, γράψτε τον ρητό βρόχο (τρέχει σήμερα και είναι ούτως ή άλλως αυτό στο οποίο αναπτύσσονται οι διακόπτες), και συμβουλευτείτε τον οδηγό One-Liners για τον πλήρη κατάλογο διακοπτών και δεκάδες συνταγές - είναι το πιο αποδοτικό πράγμα που μπορείτε να διαβάσετε ερχόμενοι από υπόβαθρο κελύφους, όπου το one-liner είναι το καθημερινό αντανακλαστικό.
Ροή ελέγχου#
Τα for, while, case και if/test του κελύφους αντιστοιχίζονται σε βρόχους και συνθήκες της Perl, και αρκετές από τις αντιστοιχίσεις είναι ευχάριστα άμεσες.
Βρόχοι. Το for f in *; do γίνεται for my $f (glob "*")· το glob αρχείων *.txt είναι glob "*.txt" ή η μορφή readline <*.txt>.
for f in /tmp/sh_*.txt; do
echo "file: $f"
done
use v5.42;
for my $f (sort glob "/tmp/sh_*.txt") {
say "file: $f";
}
# file: /tmp/sh_data.txt
# file: /tmp/sh_passwd.txt
Συνθήκες και test. Αυτή είναι μια ευτυχής αντιστοίχιση: οι έλεγχοι αρχείων [ -e ], [ -d ], [ -f ] του κελύφους είναι οι ίδιοι χειριστές -X στην Perl. Η σύγκριση συμβολοσειρών και αριθμών χωρίζεται σε δύο οικογένειες, όπως γίνεται ανάμεσα στα [ "$a" = "$b" ] και [ "$a" -eq "$b" ]: η Perl χρησιμοποιεί eq/ne για συμβολοσειρές και ==/!= για αριθμούς. Τα -z/-n (κενή / μη κενή συμβολοσειρά) του κελύφους γίνονται !length / length.
if [ -f "$f" ] && [ -n "$name" ]; then
echo ok
fi
[ "$a" = "$b" ] # string equality
[ "$x" -eq "$y" ] # numeric equality
use v5.42;
my $f = '/tmp/sh_data.txt';
my $name = 'set';
say "ok" if -f $f && length $name; # same -f, same idea
# ok
say "abc" eq "abc" ? "eq" : "ne"; # string compare
say 10 == 10.0 ? "num-eq" : "num-ne"; # numeric compare
# eq
# num-eq
case. Το case του κελύφους δεν έχει δίδυμο μίας δεσμευμένης λέξης· η ιδιωματική Perl είναι μια αλυσίδα for/if ή ένας τριαδικός καταρράκτης (ή, για πολλούς κλάδους, ένας κατακερματισμός αποστολής που αντιστοιχίζει κλειδιά σε αναφορές κώδικα).
case "$cmd" in
start) echo starting ;;
stop) echo stopping ;;
*) echo "unknown: $cmd" ;;
esac
use v5.42;
for my $cmd (qw(start stop bogus)) {
my $r =
$cmd eq 'start' ? "starting"
: $cmd eq 'stop' ? "stopping"
: "unknown: $cmd";
say "$cmd -> $r";
}
# start -> starting
# stop -> stopping
# bogus -> unknown: bogus
Κατάσταση εξόδου. Μετά από μια system(), η κατάσταση του παιδιού βρίσκεται στο $?, ακριβώς η μεταβλητή του κελύφους. Όμως το $? κρατά την ακατέργαστη κατάσταση wait, όχι τον σκέτο κωδικό εξόδου: ολισθήστε δεξιά κατά 8 για να πάρετε τον κωδικό που το κέλυφος θα εμφάνιζε ως $?. Οι δεσμευμένες λέξεις βρόχου last και next είναι το break και το continue του κελύφους.
use v5.42;
system('sh', '-c', 'exit 3');
say "raw \$? = $?"; # raw $? = 768
say "exit code = ", $? >> 8; # exit code = 3
Η παγίδα: το $? >> 8 είναι ο κωδικός εξόδου· τα χαμηλά 8 bits κρατούν το σήμα που σκότωσε το παιδί (αν υπάρχει). Το κέλυφος το κρύβει πίσω από το δικό του $?· στην Perl κάνετε εσείς την ολίσθηση. Το να το ξεχάσετε είναι ο λόγος που το if ($? == 1) μετά από μια εντολή που τερμάτισε με 1 είναι σχεδόν πάντα λάθος - η ακατέργαστη τιμή είναι 256, όχι 1.
Μεταβλητές και δεδομένα#
Μια μεταβλητή κελύφους είναι πάντα μια συμβολοσειρά - var=value, χρησιμοποιούμενη ως $var ή ${var}, και τοποθετημένη σε εισαγωγικά ως "$var" για να επιβιώσει του διαχωρισμού λέξεων. Το βαθμωτό της Perl είναι my $var, με λεξιλογική εμβέλεια, και δεν χρειάζεται κανένας χορός παράθεσης σε εισαγωγικά: ένα βαθμωτό της Perl κρατά μία τιμή και δεν ξαναδιαχωρίζεται κατά τη χρήση. Εκεί που το κέλυφος αναγκάζει τα πάντα να περάσουν μέσα από συμβολοσειρές, η Perl προσθέτει τους δύο περιέκτες που το κέλυφος ποτέ δεν είχε: διατεταγμένους @arrays και κλειδιού/τιμής %hashes.
name="Ada"
greeting="Hello, ${name}"
langs="Perl Python" # a "list" is really a string + IFS
use v5.42;
my $name = "Ada";
my $greeting = "Hello, $name"; # interpolation, like "${name}"
my @langs = ("Perl", "Python"); # a real list, not a split string
say $greeting; # Hello, Ada
say "first: $langs[0]"; # first: Perl
say "count: ", scalar @langs; # count: 2
Ορίσματα σεναρίου. Οι θεσιακές παράμετροι του κελύφους αντιστοιχίζονται στο @ARGV. Το ναρκοπέδιο παράθεσης σε εισαγωγικά του "$@" έναντι του $* απλώς δεν υπάρχει: το @ARGV είναι ένας πραγματικός πίνακας, κάθε όρισμα ένα καθαρό στοιχείο ανεξάρτητα από τα κενά ή τα glob που περιείχε. Τα $1..$9 γίνονται $ARGV[0]..$ARGV[8], το $# (το πλήθος) γίνεται scalar @ARGV, και το $0 (το όνομα του σεναρίου) γίνεται $0.
echo "count: $#"
echo "first: $1"
echo "all: $@"
use v5.42;
say "count: ", scalar @ARGV; # like $#
say "first: $ARGV[0]"; # like $1
say "all: @ARGV"; # like "$@"
# invoked as: pperl script.pl one two three
# count: 3
# first: one
# all: one two three
Συσχετιστικοί πίνακες. Ένας κατακερματισμός της Perl είναι το declare -A του κελύφους καμωμένο σωστά - δηλωμένος με %, προσπελάσιμος μία τιμή τη φορά με $h{key}, και πολύ πιο εργονομικός από τους πρόχειρα προσαρτημένους συσχετιστικούς πίνακες του κελύφους.
use v5.42;
my %count;
$count{$_}++ for qw(apple pear apple fig apple pear);
for my $k (sort keys %count) {
say "$k: $count{$k}";
}
# apple: 3
# fig: 1
# pear: 2
Η παγίδα: μια μεταβλητή κελύφους είναι εξ ορισμού καθολική και μια μεταβλητή my της Perl έχει λεξιλογική εμβέλεια στο μπλοκ της - η αντίθετη προεπιλογή. Το local του κελύφους (δυναμική εμβέλεια μέσα σε μια συνάρτηση) είναι το πλησιέστερο στο local της Perl, αλλά για συνηθισμένες μεταβλητές θέλετε το my, που είναι αληθινή λεξιλογική εμβέλεια. Και η παρεμβολή συμβαίνει μόνο σε διπλά εισαγωγικά: το "$name" παρεμβάλλει, το '$name' είναι οι κυριολεκτικοί τέσσερις χαρακτήρες, ακριβώς όπως στο κέλυφος.
Η Perl έχει επίσης πλήρεις υπορουτίνες, αναφορές, αρθρώματα και OO αν το σενάριό σας ξεπεράσει ένα one-liner - δείτε τον οδηγό Αντικειμενοστραφούς Προγραμματισμού και τους άλλους οδηγούς coming-from.
Παγίδες ερχόμενοι από το κέλυφος#
Οι παγίδες, συγκεντρωμένες για μια τελική ματιά πριν γράψετε:
Το
<$fh>κρατά την τελική νέα γραμμή. Τοreadτου κελύφους περικόπτει· η Perl όχι. Τοchompείναι το αντανακλαστικό σας σε κάθε ανάγνωση γραμμής.Το
splitείναι ξεχωριστό βήμα, και είναι μηδενοβάσει. Τα πεδία είναι$f[0],$f[1], όχι το βάσει-του-ένα$1τουawk. Τοsplit ' '(μαγικά κενά) περικόπτει και συμπτύσσει· τοsplit / /(ένα κυριολεκτικό κενό) όχι.Το
s///επιστρέφει ένα πλήθος, όχι τη νέα συμβολοσειρά. Τοmy $x = $s =~ s/a/b/βάζει το πλήθος αντικαταστάσεων στο$x. Αντιγράψτε πρώτα ((my $t = $s) =~ s/.../.../) ή χρησιμοποιήστε τη σημαία/r.Το
$? >> 8είναι ο κωδικός εξόδου. Το ακατέργαστο$?μετά τηsystemείναι η πλήρης κατάσταση wait· ο κωδικός εξόδου είναι τα υψηλά 8 bits. Τοif ($? == 1)είναι σχεδόν πάντα σφάλμα· θέλετεif (($? >> 8) == 1).Η
openδεν εγείρει σφάλμα σε αποτυχία. Χρησιμοποιήστεopen my $fh, '<', $path or die "...: $!". Το$!είναι η συμβολοσειρά σφάλματος του συστήματος - η σιωπηλή αποτυχία του κελύφους γινόμενη ρητή.Προτιμήστε τη μορφή λίστας των
system/openέναντι μιας συμβολοσειράς με παρεμβολή. Τοsystem('ls', '-l', $dir)δεν τρέχει κανένα κέλυφος: δεν υπάρχει διαχωρισμός λέξεων, ούτε έκπληξη glob, ούτε κόλαση παράθεσης. Η μορφή συμβολοσειράς (system "ls -l $dir") επανεισάγει κάθε παγίδα παράθεσης κελύφους από την οποία ήρθατε εδώ να ξεφύγετε.Το
myείναι λεξιλογικό· οι μεταβλητές κελύφους είναι καθολικές. Δηλώστε μεmyκαι περιορίστε την εμβέλεια στο μπλοκ· καταφύγετε στοlocalμόνο για τη συμπεριφορά δυναμικής εμβέλειας του κελύφους, που σπάνια είναι αυτό που θέλετε.Η σύγκριση συμβολοσειρών έναντι αριθμών είναι δύο οικογένειες.
eq/neγια συμβολοσειρές,==/!=για αριθμούς - ο διαχωρισμός[ = ]έναντι[ -eq ], αλλά οι χειριστές είναι αντεστραμμένοι σε σχέση με το κέλυφος, οπότε διαβάστε τους προσεκτικά.Οι διακόπτες έμμεσου βρόχου (
-n/-p/-a/-F/-i/-l) δεν αναγνωρίζονται ακόμη από αυτό το build τουpperl. Μόνο το-eλειτουργεί σήμερα. Γράψτε τον ρητό βρόχοwhile (<$fh>) { ... }, που είναι ακριβώς αυτό στο οποίο αναπτύσσονται οι διακόπτες, μέχρι να προστεθούν.