Είσοδος προγράμματος - @ARGV, %ENV, @INC#
Τρεις καθολικές μεταβλητές μεταφέρουν είσοδο στο πρόγραμμα: ορίσματα γραμμής εντολών, μεταβλητές περιβάλλοντος, και τη διαδρομή αναζήτησης αρθρωμάτων. Μοιράζονται αρκετό πλαίσιο ώστε οι αποφάσεις για μία συνήθως ενημερώνουν τις άλλες - η @ARGV αλληλεπιδρά με τη $0, τα κλειδιά σχετικά με Perl στη %ENV επηρεάζουν τη @INC, και το ερώτημα σειράς της @INC αντικατοπτρίζεται σε παρόμοιες ανησυχίες για καταναλωτές @ARGV και το PATH του κελύφους.
Μεταβλητή | Κρατά |
|---|---|
| Ορίσματα γραμμής εντολών (χωρίς το όνομα σεναρίου) |
| Όνομα σεναρίου (σε ζεύγος με τη |
| Περιβάλλον διεργασίας |
| Διαδρομή αναζήτησης αρθρωμάτων (χρησιμοποιείται από |
| Ήδη φορτωμένα αρθρώματα |
| Τρέχων δείκτης |
Η @ARGV και η σχέση της με τη $0#
Μετά την εκκίνηση της Perl, το όνομα σεναρίου βρίσκεται στη $0 και τα ορίσματα του σεναρίου στη @ARGV:
$ pperl myscript.pl -v --output /tmp/x foo bar
print "$0\n"; # myscript.pl
print "@ARGV\n"; # -v --output /tmp/x foo bar
print scalar(@ARGV), " args\n"; # 5 args
Η $ARGV[0] είναι το πρώτο όρισμα του σεναρίου, όχι το ίδιο το όνομα σεναρίου. Αυτό διαφέρει από το argv της C (όπου argv[0] είναι το όνομα προγράμματος)· η Perl εκθέτει το όνομα προγράμματος ξεχωριστά μέσω $0.
Τυπικός χειρισμός ορισμάτων#
Η χειρόγραφη ανάλυση ορισμάτων είναι εντάξει για τετριμμένα σενάρια:
my %opt;
while (@ARGV && $ARGV[0] =~ /^-/) {
my $flag = shift @ARGV;
last if $flag eq '--';
if ($flag eq '-v') { $opt{verbose} = 1 }
elsif ($flag eq '--output'){ $opt{output} = shift @ARGV }
else { die "unknown flag: $flag\n" }
}
my @files = @ARGV;
Για οτιδήποτε πέρα από απλοϊκά σενάρια, το άρθρωμα Getopt::Long χειρίζεται κάθε τυπικό μοτίβο (μεγάλες επιλογές, συντομογραφία, υποχρεωτικά ορίσματα, επαναλαμβανόμενες σημαίες, διαχωριστής --, αυτόματη βοήθεια):
use Getopt::Long;
my %opt;
GetOptions(
'verbose|v' => \$opt{verbose},
'output|o=s' => \$opt{output},
'help|h' => \$opt{help},
) or die "bad options; try --help\n";
my @files = @ARGV; # what's left after option processing
Η GetOptions τροποποιεί τη @ARGV επιτόπια - όταν επιστρέψει, η @ARGV περιέχει μόνο τα ορίσματα που δεν είναι επιλογές.
Η @ARGV και ο τελεστής «διαμάντι»#
Ο τελεστής <> (διαμάντι) επαναλαμβάνει γραμμές από τα αρχεία που ονομάζονται στη @ARGV, χρησιμοποιώντας εφεδρικά το STDIN αν η @ARGV είναι κενή:
while (<>) {
# one line from one of the @ARGV files (or STDIN)
chomp;
process($_);
}
Αυτή είναι η προεπιλεγμένη σύμβαση κάθε προγράμματος φίλτρου τύπου awk. Η <> ανοίγει κάθε αρχείο διαδοχικά, θέτει τη $ARGV στο όνομά του κατά την ανάγνωση, και το κλείνει στο EOF.
Η -i και οι άλλοι διακόπτες - (-n, -p, -l, -a) χτίζουν όλοι τη συμπεριφορά τους πάνω σε αυτό το μοτίβο @ARGV → <>.
Τροποποίηση @ARGV πριν τη <>#
Μπορείτε να προ-επεξεργαστείτε τη @ARGV για να αλλάξετε τι διαβάζει η <>:
# Add an extra file to the front of the argv list:
unshift @ARGV, 'header.txt';
# Process compressed files transparently by rewriting argv:
@ARGV = map { /\.gz$/ ? "gunzip -c $_ |" : $_ } @ARGV;
while (<>) {
process($_);
}
Η μορφή cmd | μετατρέπει το όρισμα σε open-από-σωλήνα-εντολής· αυτό είναι ένα παλιό ιδίωμα Perl για διαφανή αποσυμπίεση σε σενάρια τύπου awk.
%ENV - το περιβάλλον διεργασίας#
Η %ENV είναι το hash μεταβλητών περιβάλλοντος που κληρονομούνται από τη γονική διεργασία (τυπικά το κέλυφος). Η ανάγνωση είναι απλή:
my $home = $ENV{HOME};
my $term = $ENV{TERM} // 'dumb';
my $path = $ENV{PATH};
Η εγγραφή αλλάζει το περιβάλλον που βλέπουν οι διεργασίες-παιδιά που αυτό το σενάριο Perl δημιουργεί στη συνέχεια:
$ENV{LC_ALL} = 'C'; # POSIX locale for child commands
$ENV{TZ} = 'UTC';
$ENV{LANG} = 'C';
delete $ENV{LC_NUMERIC}; # remove a key entirely
system('date'); # child sees the modified ENV
Η αλλαγή δεν διαδίδεται πίσω στον γονέα - το κέλυφος που εκκίνησε το σενάριο κρατά το αρχικό του περιβάλλον. Αν χρειάζεστε το γονικό κέλυφος να πάρει νέες τιμές, γράψτε τες σε αρχείο ή ζητήστε από το κέλυφος να κάνει eval την έξοδό σας (το τυπικό μοτίβο dotenv).
Μετατροπή τιμών σε συμβολοσειρά#
Από την Perl 5.18, οι τιμές %ENV μετατρέπονται πάντα σε συμβολοσειρά κατά τον χρόνο ανάθεσης. Η αποθήκευση αναφοράς δεν κάνει πλέον πλήρη κύκλο:
my $arr = [1, 2, 3];
$ENV{DATA} = $arr;
print $ENV{DATA}; # "ARRAY(0x...)" - stringified
Οι μεταβλητές περιβάλλοντος είναι μια διεπαφή τύπου συμβολοσειράς με το λειτουργικό σύστημα· η διατήρηση δομής μέσω ορίου fork/exec δεν θα λειτουργούσε ούτως ή άλλως. Σειριοποιήστε ό,τι μη τετριμμένο ως JSON ή παρόμοιο πριν την αποθήκευση.
Μεταβλητές περιβάλλοντος ειδικές της Perl#
Ένα μικρό σύνολο μεταβλητών περιβάλλοντος τηρείται από την ίδια τη Perl, όχι από σενάρια χρηστών:
Μεταβλητή | Αποτέλεσμα |
|---|---|
| Διαδρομές χωρισμένες με άνω-κάτω τελεία που προστίθενται στην αρχή της |
| Ίδια, παλαιότερη μεταβλητή· χρησιμοποιείται μόνο αν η |
| Διακόπτες που εφαρμόζονται σαν να ήταν στη γραμμή εντολών (μόνο υποσύνολο) |
| Επιλογές για τον αποσφαλματωτή |
| Προεπιλεγμένες ρυθμίσεις στρώματος UTF-8 (αντικατοπτρίζει τη |
| Επαναπρόσθεση |
| Σπόρος τυχαιοποίησης hash ανά εκτέλεση |
|
|
| Διαβάζεται από την επέκταση glob |
| Αναζητείται από |
Σε tainted mode (-T), οι PERL5LIB, PERL5OPT, και PERLLIB αγνοούνται για αποτροπή κλιμάκωσης προνομίων. Δείτε τον οδηγό διακοπτών γραμμής εντολών για το πλήρες μοντέλο ασφαλείας.
delete $ENV{KEY} έναντι $ENV{KEY} = undef#
Τα δύο δεν είναι ισοδύναμα:
delete $ENV{KEY}; # KEY is not in the environment
$ENV{KEY} = undef; # KEY is in the environment, value ""
A child process started after delete will not see KEY at all
getenv("KEY")returns NULL. Afterundefassignment, the child seesKEY=""(empty string). Code that distinguishes «unset» from «set to empty» cares about this difference.
@INC - η διαδρομή αναζήτησης αρθρωμάτων#
Όταν η use και η require αναζητούν ένα άρθρωμα, διασχίζουν τη @INC από αριστερά προς τα δεξιά και σταματούν στην πρώτη αντιστοιχία:
print "$_\n" for @INC;
# /usr/local/lib/perl5/site_perl/5.42.0/x86_64-linux
# /usr/local/lib/perl5/site_perl/5.42.0
# /usr/local/lib/perl5/5.42.0/x86_64-linux
# /usr/local/lib/perl5/5.42.0
# (no trailing "." since Perl 5.26)
Η @INC αρχικοποιείται κατά την εκκίνηση από συνδυασμό:
Ενσωματωμένες προεπιλογές (οι
installprivlib,installsitelib, … μεταγλώττισης).Διακόπτες
-I /pathστη γραμμή εντολών, προστίθενται στην αρχή κατά σειρά.Η μεταβλητή περιβάλλοντος
PERL5LIB(ήPERLLIB), επίσης στην αρχή.
Μετά την εκκίνηση είναι απλώς ένας κανονικός πίνακας. Η σειρά μετράει: μια αντιστοιχία σε πρώιμο στοιχείο υπερισχύει.
use lib έναντι unshift @INC έναντι push @INC#
Τρεις τρόποι προσθήκης διαδρομής. Δεν είναι ισοδύναμοι:
use lib '/my/lib'; # unshift at compile-time
unshift @INC, '/my/lib'; # unshift at runtime
push @INC, '/my/lib'; # push at runtime
Οι διαφορές:
Η
use libεκτελείται κατά τη μεταγλώττιση. Συμβαίνει πριν από κάθε δήλωσηuseπου την ακολουθεί στη σελίδα. Αυτό είναι αυτό που σχεδόν πάντα θέλετε όταν λέτε «αυτό το σενάριο χρειάζεται προσαρμοσμένο κατάλογο βιβλιοθήκης»:use lib '/opt/myapp/lib'; use MyApp::Module; # found because lib was unshifted first
Εσωτερικά η
use lib '/path'κάνειBEGIN { unshift @INC, '/path' }, οπότε η νέα διαδρομή αναζητείται πρώτη - ένα αντίγραφο αρθρώματος στο/opt/myapp/libεπισκιάζει ένα στη@INCσυστήματος.Η
unshift @INC, '/path'στο ανώτατο επίπεδο έχει σημασιολογία χρόνου εκτέλεσης. Λειτουργεί για κλήσειςrequireπου συμβαίνουν μετά τηunshift, αλλά μια δήλωσηuseσε μεταγενέστερη γραμμή του ίδιου αρχείου δεν θα τη δει (επειδή ηuseείναι η ίδια κατά τη μεταγλώττιση):unshift @INC, '/my/lib'; use Some::Module; # NOT found in /my/lib - too early
Αυτή είναι μια συχνή παγίδα. Η
BEGIN { unshift @INC, '/my/lib' }τη διορθώνει· ηuse libείναι η καθαρότερη γραφή ακριβώς αυτού.push @INC, '/path'appends. The new path is searched last. This is the right choice when you want your library to be a fallback - used only if no other location has the modulefor example, providing a built-in version of a CPAN module that the user might have installed on the system.
Ο πρακτικός κανόνας: χρησιμοποιείτε πάντα use lib για «θέλω η διαδρομή αρθρωμάτων του κώδικά μου να ισχύει κατά τη μεταγλώττιση», κάτι που ισχύει σχεδόν σε κάθε περίπτωση.
Hooks @INC#
Η @INC μπορεί να περιέχει όχι μόνο διαδρομές αλλά και αναφορές κώδικα και blessed αντικείμενα. Όταν η require συναντά ένα από αυτά, το καλεί ως hook - περνώντας τη ζητούμενη διαδρομή αρθρώματος - και το hook επιστρέφει είτε ανοιχτό filehandle είτε λίστα πηγαίου κώδικα, γεννήτριες, κ.λπ. Έτσι μηχανισμοί όπως Module::Pluggable, PAR, και Test::MockModule υποκλέπτουν τη φόρτωση αρθρωμάτων.
Το πλήρες πρωτόκολλο hook τεκμηριώνεται στη require. Οι περισσότεροι χρήστες δεν γράφουν ποτέ ένα· τα διαβάζετε όταν αποσφαλματώνετε προβλήματα «γιατί αυτό το άρθρωμα δεν φορτώνεται από εκεί που περιμένω».
%INC - η cache φορτωμένων αρθρωμάτων#
Μετά την επιτυχή φόρτωση ενός αρθρώματος, η %INC το καταγράφει. Το κλειδί είναι η διαδρομή που ζητήθηκε να βρει η Perl (π.χ. Foo/Bar.pm)· η τιμή είναι η απόλυτη διαδρομή που ικανοποίησε το αίτημα:
use Data::Dumper;
print "Data::Dumper loaded from $INC{'Data/Dumper.pm'}\n";
# /usr/local/lib/perl5/site_perl/5.42.0/Data/Dumper.pm
# Show every loaded module:
for my $key (sort keys %INC) {
print "$key → $INC{$key}\n";
}
Η require ελέγχει τη %INC πριν διασχίσει τη @INC - μια δεύτερη require Foo; είναι no-op επειδή η Foo.pm βρίσκεται ήδη στη cache. Για εξαναγκασμό εκ νέου require:
delete $INC{'Foo.pm'};
require Foo; # actually re-runs Foo.pm
Αυτό είναι το τυπικό ιδίωμα για δοκιμή συμπεριφοράς επαναφόρτωσης. Λάβετε υπόψη ότι η εκ νέου εκτέλεση αρχείου αρθρώματος δεν αναιρεί ό,τι δημιούργησε η πρώτη εκτέλεση - πακέτα, subs, και καθολικές μεταβλητές παραμένουν.
$INC - ο δείκτης μέσα σε hook @INC#
Διαθέσιμο από την Perl 5.37.7. Όταν καλείται ένα hook @INC, η $INC τίθεται στον δείκτη του hook στη @INC. Μετά την επιστροφή του hook, ο επαναλήπτης προχωρά βάσει $INC + 1 - οπότε το hook μπορεί να ξαναγράψει τη @INC και να κατευθύνει την αναζήτηση σε συγκεκριμένη θέση στη συνέχεια. Αυτό είναι προχωρημένο χαρακτηριστικό· ο καθημερινός κώδικας δεν το αγγίζει ποτέ.
Δείτε επίσης#
use lib- η κανονική γραφή μεταγλώττισης για προσθήκη στην αρχή της@INC.Getopt::Long- ο τυπικός αναλυτής για τη@ARGV.readline- η μορφή συνάρτησης του τελεστή «διαμάντι»<>που κινεί το φιλτράρισμα@ARGV → STDIN.-i,-n,-p,-a- διακόπτες γραμμής εντολών χτισμένοι πάνω στη@ARGV.Μεταβλητές διεργασίας - η
$0είναι το όνομα σεναρίου· η$$είναι το PID· ζευγαρώστε τα με τη@ARGVγια αυτο-επανεκκίνηση και καταγραφή.Μονόγραμμα γραμμής εντολών - ο οδηγός που δείχνει κάθε συνδυασμό
@ARGV/<>/διακόπτη σε πλαίσιο.