אם כתבתם תוכנית בפרל, למשלprogramming.pl, המשתמשים שלכם יכולים להריץ את התוכנית משורת הפקודה כך:perl programming.pl.
כמו כן הם יכולים גם להעביר לתוכנית ארגומנטים בשורת הפקודה כך: perl programming.pl -a --machine remote /etc. אי אפשר למנוע זאת מהמשתמשים, והתוכנית תתעלם מהארגומנטים הנוספים. השאלה היא איך אתה, המתכנת, יכול לדעת אילו ערכים הועברו, ואם בכלל הועברו?
שורת הפקודה
פרל מספקת לנו אוטומטית מערך בשם @ARGV
, שמחזיק את כל הערכים משורת הפקודה.
אין צורך להצהיר על המשתנה, אפילו אם משתמשים ב-: use strict
.
המשתנה הזה תמיד קיים, והערכים משורת הפקודה מוצבים בו אוטומטית.
אם אין פרמטרים, המערך יהיה ריק. אם יש פרמטר אחד, אז ערכו יהיה האלמנט היחיד ב- @ARGV
. בדוגמה שראינו קודם, המערך @ARGV
יכיל את האלמנטים הבאים:
-a, --machine, remote, /etc
נראה זאת בפעולה:
שמור את שורות הקוד הבאות כ - programming.pl:
use strict;
use warnings;
use Data::Dumper qw(Dumper);
print Dumper \@ARGV;
הרץ את התוכנית כך: perl programming.pl -a --machine remote /etc
וזה מה שיודפס
$VAR1 = [
'-a',
'--machine',
'remote',
'/etc'
];
כפי שניתן לראות השתמשנו בפונקציית ה- Dumper
של המודול Data::Dumper
להדפסת התוכן של @ARGV
מי שבא משפת תיכנות אחרת, ודאי שואל את עצמו איפה שמה של תוכנית הפרל?
שמה של התוכנית הוא במשתנה $0
שמה של התוכנית שכרגע רצה, במקרה זה programming.pl, תמיד נמצא במשתנה$0
. (שימו לב, המשתנה הזה אינו קשור ל-, $1
, $2
, וכו'!)
מתכנתי C
אם אתם מכירים את שפת התיכנות C, אז זה דומה ל- argv, פרט לכך שהמערך
@ARGV
בפרל אינו כולל את שם התוכנית.
ניתן למצוא אותו במשתנה $0
. בנוסף לך, משתנה כמו argcאינו נחוץ,
ניתן לקבל בקלות את מספר האלמנטים במערך @ARGV
באמצעות הפונקציה scalar
או על ידי השמת המערך
בהקשר סקלרי.
תיכנות במעטפת יוניקס/לינוקס Unix/Linux Shell
אם אתם מגיעים מעולם תיכנות ב-Unix/Linux Shell ודאי תזהו שהמשתנה $0
משמש גם שם כשמו של הסקריפט. ב-shell המשתנים $1
, $2
, etc.
מכילים את שאר הפרמטרים משורת הפקודה. בפרל משתנים אילו משמשים את הביטויים הרגולריים. הפרמטרים משורת הפקודה נמצאים במערך @ARGV
. בדומה למשתנה $*
ב-Unix/Linux shell.
כיצד לחלץ את ערכי הפרמטרים משורת הפקודה מהמערך @ARGV
@ARGV
הוא פשוט מערך הפרל.
ההבדל היחי בינו לבין מערכים שאתם יוצרים הוא שאין צורך להצהיר עליו ופרל מציבה בו ערכים עם תחילת הרצת התוכנית שלכם.
פרט להבדלים אילו, אתם יכולים להתייחס אליו כאל מערך רגיל.
אתם יכולים לעבור על כל האלמנטים באמצעות foreach
, או לגשת אליהם אחד אחד באמצעות אינדקס: $ARGV[0]
.
אתם יכולים להפעיל את הפונקציות shift, unshift, pop or push על המערך.
אפשר לא רק לקרוא את תוכנו של @ARGV
, אפשר גם לשנות אותו.
אם אתם מצפים לקבל ערך יחיד על שורת הפקודה ואתם רוצים לבדוק מהו, או אם הוא בכלל הועבר, אתם יכולים להסתכל על $ARGV[0]
. אם אתם מצפים לשני ערכים אז תבדקו גם את $ARGV[1]
.
לדוגמה, ניצור ספר טלפונים. אם מספקים לתוכנית שם אחד, היא תדפיס את מספר הטלפון המתאים. אם נותנים לה שם ומספר, היא תשמור את הערכים ב"מסד הנתונים" שלה. (לא נטפל כאן בחלק של "מסד הנתונים" רק נעשה כאילו יש לנו משהו.)
אנחנו יודעים שהפרמטרים יתקבלו ב - $ARGV[0]
ואולי גם ב- $ARGV[1]
, אבל אין להם שום משמעות מעבר לכך שהם שני האלמנטים הראשונים של מערך.
לרוב רצוי להשתמש במשתנים משלכם, עם שמות, במקום ב- $ARGV[0] וכדומה.
הדבר הראשון שנעדה הוא להעתיק את הערכים למשתנים עם שמות מייצגים.
משהו כזה יכול לעבוד:
my $name = $ARGV[0];
my $number = $ARGV[1];
אבל זה נראה יותר יפה:
my ($name, $number) = @ARGV;
נראה עכשיו את הדוגמה במלואה (פרט לחלק של מסד הנתונים) שימרו את הקוד הבא ב-programming.pl.
use strict;
use warnings;
my ($name, $number) = @ARGV;
if (not defined $name) {
die "Need name\n";
}
if (defined $number) {
print "Save '$name' and '$number'\n";
# save name/number in database
exit;
}
print "Fetch '$name'\n";
# look up the name in the database and print it out
לאחר העתקת הערכים מ- <אנחנו בודקים אם ניתן שם.
אם לא, אנחנו קוראים ל- die
שתדפיס הודעת שגיאה ותפסיק את ההרצה.
אם היה שם, אנחנו בודקים אם היה גם מספר. אם היה גם מספרף אנחנו שומרים אותו במסד הנתונים (היישום שלו אינו חלק מהקוד הנ"ל) ומסיימים את ההרצה.
אם לא היה מספר אנחנו מאחזרים אותו ממסד הנתונים. (שוב, לא מייושם כאן.)
נראה איך זה עובד: (סימן ה- $ רק מסמן את המקום לכתוב את הפקודות, אנחנו לא מקלידים אותו.)
$ perl programming.pl Foo 123
Save 'Foo' and '123'
$ perl programming.pl Bar 456
Save 'Bar' and '456'
$ perl programming.pl John Doe 789
Save 'John' and 'Doe'
שתי הקריאות הראשונות היו בסדר, אבל השלישית לא נראית כל כך טוב. רצינו לשמור את מספר הטלפון של John Doe כ-789, אבל במקום זה התוכנית שלנו שמרה את מספר הטלפון של John כאילו הוא "Doe".
הסיבה לכך היא פשוטה ואין לכך שום קשר לפרל. זה יעבוד באותה צורה בכל שפה.
ה-shell שורת הפקודה, שממנה מריצים את התוכנית מפרקת את השורה ונותנת לפרל את הערכים, והיא בתורה מציבה אותם ב-
@ARGV
. גם ה-shell של לינוקס וגם שורת הפקודה של Windows מפצלות את שורת הפקודה בכל רווח. לכן כשהקלדנו perl programming.pl John Doe 789
, למעשה הועברו שלושה פרמטרים לתוכנית שלנו. כדי לגרום לתוכנית לעבוד נבון, המשתמש צריך להכניס את הערכים שכוללים רווחים בתוך גרשיים.
$ perl a.pl "John Doe" 789
Save 'John Doe' and '789'
כמתכנתים, אין לכם הרבה מה לעשות נגד זה.
בדיקת הפרמטרים.
אולי תוכלו לבדוק אם מספר האלמנטים אינו עולה על המספר שאתם מצפים לו. הבדיקה תמנע מהמשתמש לעשות את הטעות שראינו. אבל אם המשתמש רוצה לאחזר את מספר הטלפון של John Doe ושוכח את הגרשיים.
perl a.pl John Doe
Save 'John' and 'Doe'
במקרה זה יש שני פרמטרים, וזהו מספר הפרמטרים הנכון.
גם כאן תוכלו לעשות שיפור קל ולבדוק אם התוכן של המשתנה$number
הוא בפורמט שאתם מוכן לקבל כמספר טלפון. כך ניתן לצמצם את הסיכוי לשגיאות במקרה זה.
גם אז התוכנית עדיין לא תהיהי מושלמת וודאי שהיא לא תהווה פתרון אוניברסלי: ביישומים אחרים ייתכן שיהיה מספר פרמטרים עם אותם אילוצים.
לצערנו, אין הרבה מה לעשות כאנחנו מפענחים את מערך @ARGV
"ידנית".
במאמר אחר באתר זה אכתוב על Getopt::Long
ועל ספריות נוספות
שמקלות על החיים, אך כעת נסתכל על מקרה פשוט נוסף.
שימוש ב-shift לקריאת פרמטר יחיד
אחד המקרים הנפוצים הוא כשמצפים מהמשתמש לספק שם קובץ יחיד על שורת הפקודה. במקרה זה אפשר לכתוב את הקוד הבא:
my $filename = shift or die "Usage: $0 FILENAME\n";
נפצל את השורה לשני חלקים כדי להקל על ההסבר:
my $filename = shift
בדרך כלל, פונקציית shift מקבלת מערך כפרמטר, אך השתמשנו בא בלי פרמטר. במקרה כזה ברירת המחדל של shift היא לפעול על
@ARGV
. שורת הקוד מעבירה את הערך הראשון של @ARGV
למשתנה$filename
. (לפחות כשהקוד הוא לא חלק מפונקציה)
כך שלפנינו הקוד הבא:
$filename or die "Usage: $0 FILENAME\n"
זהו ביטוי בוליאני boolean .
אם המשתנה $filename
מכיל את שם הקובץ
אז ערכו הבוליאני יהיה True והתוכנית תמשיך לרוץ בלי לבצע את החלק or die ...
part.
אם @ARGV
ריק אז המשתנה$filename
מקבל את הערך undef
,
ערכו הבוליאני של הביטוי יהיה False
ופרל תבצע את חלקה הימני של שורת ה- or
,
היא תדפיס הודעה ותפסיקאת הרצת התוכנית.
מכאן שהקוד שראינו בודק אם ניתן ערך על שורת הפקודה. הערך מועתק אל$filename
. אם אין ערך, התכנית מסיימת עם die
.
באג קטן
יש באג אחד קטן בקוד. אם המשתמש מספק 0 כשם הקובץ ערכו הבוליאני גם הוא False וזה ייראה כאילו לא הועבר ערך והתוכנית תסרב לגעת בקובץ עם שם זה. השאלה היא: האם זה משנה? האם נסתדר עם העבודה שהתוכנית שלנו לא יכולה לטפל בקובץ שנקרא 0... ?
מקרים מורכבים
קיימים מקרים רבים נוספים הרבה יותר מורכבים מהמקרים שראינו עד כה
בשביל מקרים כאלו ודאי תירצו להשתמש בכלי כמו Getopt::Long
שיודע לנתח את התוכן של @ARGV
על פי הצהרה כלשהי של סוג הפרמטרים שאתם מוכנים לקבל.