קלט תוכנית - @ARGV, %ENV, @INC#
שלושה גלובליים נושאים קלט אל התוכנית: ארגומנטים של שורת הפקודה, משתני סביבה, ונתיב חיפוש המודולים. הם חולקים מספיק הקשר כך שהחלטות לגבי אחד בדרך כלל מיידעות את האחרים - @ARGV מקיים אינטראקציה עם $0, המפתחות הרלוונטיים־ל־Perl של %ENV משפיעים על @INC, ושאלת הסדר של @INC משתקפת בדאגות דומות עבור צרכני @ARGV ו־PATH של ה־shell.
משתנה | מחזיק |
|---|---|
| ארגומנטים של שורת הפקודה (למעט שם הסקריפט) |
| שם הסקריפט (מזווג עם |
| סביבת התהליך |
| נתיב חיפוש מודולים (משמש את |
| מודולים שכבר נטענו |
| אינדקס |
@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 | הופכת את הארגומנט לפתיחת pipe־מפקודה; זהו ניב Perl ותיק עבור דחיסה־מבוטלת שקופה בסקריפטים בסגנון awk.
%ENV - סביבת התהליך#
%ENV הוא האש משתני הסביבה שנירשו מתהליך ההורה (בדרך כלל ה־shell). הקריאה פשוטה:
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
השינוי אינו מתפשט בחזרה אל ההורה - ה־shell שהתחיל את הסקריפט שומר על סביבתו המקורית. אם אתם זקוקים לכך שה־shell ההורה יקלוט ערכים חדשים, כתבו אותם לקובץ או גרמו ל־shell לבצע 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 ברירת מחדל (משקפות את |
| הוספה מחדש של |
| זרע אקראיות האש לכל־ריצה |
|
|
| נקרא על ידי הרחבת glob של |
| נחפש על ידי |
במצב taint (-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 עבור ”אני רוצה שנתיב המודול של הקוד שלי ייכנס לתוקף בזמן הידור“, שזה כמעט כל מקרה.
הוּוי @INC#
@INC עשוי להכיל לא רק נתיבים אלא גם הפניות קוד ועצמים blessed. כאשר require נתקל באחד מאלה, הוא קורא לו כהוו - מעביר את נתיב המודול המבוקש - וההוו מחזיר או מטפל קובץ פתוח או רשימה של קוד מקור, מחוללים, וכו«. כך מנגנונים כמו Module::Pluggable, PAR, ו־Test::MockModule מיירטים את טעינת המודולים.
פרוטוקול ההוו המלא מתועד תחת require. רוב המשתמשים לעולם אינם כותבים אחד; אתם קוראים עליהם בעת ניפוי בעיות מסוג ”מדוע מודול זה אינו נטען מהמקום שאני מצפה“.
%INC - מטמון המודולים הטעונים#
לאחר שמודול נטען בהצלחה, %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 כבר במטמון. כדי לכפות require מחדש:
delete $INC{'Foo.pm'};
require Foo; # actually re-runs Foo.pm
זהו הניב התקני לבדיקת התנהגות טעינה מחדש. שימו לב שהרצה מחדש של קובץ מודול אינה מבטלת את הגדרת מה שהריצה הראשונה יצרה - חבילות, תת־שגרות, וגלובליים נשארים.
$INC - האינדקס בתוך הוו @INC#
זמין מאז Perl 5.37.7. כאשר הוו @INC נקרא, $INC נקבע לאינדקס של ההוו ב־@INC. לאחר שההוו חוזר, האיטרטור מתקדם על בסיס $INC + 1 - כך שההוו יכול לכתוב מחדש את @INC ולכוון את החיפוש למיקום ספציפי לאחר מכן. זוהי תכונה מתקדמת; קוד יומיומי לעולם אינו נוגע בה.
ראו גם#
use lib- הכתיב הקנוני בזמן הידור עבור הקדמה ל־@INC.Getopt::Long- הפענח התקני עבור@ARGV.readline- צורת הפונקציה של אופרטור היהלום<>שמניע את הסינון@ARGV → STDIN.-i,-n,-p,-a- מתגי שורת פקודה הבנויים על@ARGV.משתני תהליך -
$0הוא שם הסקריפט;$$הוא ה־PID; זווגו אותם עם@ARGVעבור הפעלה־מחדש־עצמית ורישום.שורות־פקודה חד־שורתיות - ההדרכה המציגה כל שילוב
@ARGV/<>/מתג בהקשר.