Phasers: BEGIN, UNITCHECK, CHECK, INIT, END#
חמישה בלוקים בעלי שם מיוחד מאפשרים לקוד להתחבר למעברים שתוכנית עושה בין היותה מהודרת לבין היותה מורצת. הם נראים כמו שגרות אך אינם כאלה: ניתן לכתוב כמה מכל סוג, לאף אחד אין שם הניתן לקריאה, וכל אחד מהם נורה אוטומטית ברגע שלו.
BEGIN { ... } # during compilation, as soon as defined
UNITCHECK { ... } # right after this compilation unit is compiled
CHECK { ... } # after the whole program is compiled
INIT { ... } # just before the runtime begins
END { ... } # just before the interpreter exits
התחילית sub (sub BEGIN { ... }) מתקבלת אך אינה מוסיפה דבר ואין לעודד אותה. אלה בלוקים, לא שגרות בעלות שם; לא ניתן לקרוא להם, אלא רק לתת להם להירות.
מתי כל אחד רץ#
תוכנית פרל חיה בשני שלבים: שלב הידור שמפרש את המקור לצורה פנימית, ושלב ריצה שמבצע אותו. ה-phasers מסמנים את הגבול.
BEGINרץ ברגע שסוגר הסוגר שלו מפורש, עוד לפני ששאר הקובץ בכלל נקרא. כך פועלuse:useהואrequireעטוף ב-BEGINבתוספתimport, כך שהשמות המיובאים קיימים עבור הקוד שלאחר מכן. לאחר ש-BEGINרץ, הוא אינו מוגדר וזיכרונו משוחזר.UNITCHECKרץ מיד כשיחידת ההידור שמכילה אותו סיימה להתהדר. יחידה היא התוכנית הראשית, מודול שנטען על ידיrequire,evalשל מחרוזת,doFILE, או קוד שהודר על ידי מבנה ה-regex(?{ }). משום שיחידות מסיימות להתהדר בזמנים שונים,UNITCHECKהוא ה-phaser היחיד שתזמונו עוקב אחר הקובץ הבודד ולא אחר התוכנית בכללותה.CHECKרץ פעם אחת, אחרי ששלב ההידור הראשוני של כל התוכנית מסתיים ולפני ששלב הריצה מתחיל.INITרץ פעם אחת, רגע לפני ששלב הריצה מתחיל, אחרי כל בלוקי ה-CHECK.ENDרץ פעם אחת, מאוחר ככל האפשר: אחרי שהתוכנית סיימה (כולל אחריdie), רגע לפני שהמפרש יוצא.
BEGIN ו-UNITCHECK קשורים לפירוש, לא לשלב המפרש, ולכן הם יכולים להירות במהלך כל שלב: eval של מחרוזת בזמן ריצה עדיין מהדר את גופו, וכל BEGIN או UNITCHECK שבתוכו רץ אז ושם. CHECK ו-INIT, לעומת זאת, מסמנים את גבול ההידור/הריצה החד-פעמי של התוכנית הראשית.
ריבוי וסדר ביצוע#
ניתן לכתוב כל מספר של כל בלוק. כולם רצים, בסדר מוגדר:
BEGIN: ראשון נכנס, ראשון יוצא (FIFO) כפי שנתקלים בו במהלך ההידור. ה-BEGINהראשון בסדר המקור רץ ראשון.UNITCHECK: אחרון נכנס, ראשון יוצא (LIFO) בתוך כל יחידה, אחרי שאותה יחידה מתהדרת.CHECK: LIFO, אחרי כל ההידור.INIT: FIFO, רגע לפני זמן הריצה.END: LIFO ביציאה. ה-ENDהאחרון שהוגדר הוא הראשון לרוץ.
המקרה המאלף הוא קובץ שמערבב משפטים רגילים עם כל חמשת סוגי הבלוקים. הבלוקים אינם רצים במקום שבו הם נכתבים; הם רצים בשלב שלהם, בסדר שלהם בכל שלב, בעוד המשפטים הרגילים רצים בין לבין בזמן ריצה. מבחינה רעיונית, עבור קובץ בודד:
1. BEGIN blocks -- FIFO, during compilation
2. UNITCHECK blocks -- LIFO, after this unit compiles
3. CHECK blocks -- LIFO, after all compilation
4. INIT blocks -- FIFO, just before runtime
5. ordinary code -- run phase, in source order
6. END blocks -- LIFO, at exit
כך ש-BEGIN ו-UNITCHECK רצים לפני כל CHECK; CHECK רץ לפני INIT; INIT רץ לפני השורה הראשונה של קוד רגיל; וכל END רץ אחרי האחרונה. בתוך BEGIN ו-INIT, בלוקים שנכתבו מוקדם יותר רצים מוקדם יותר; בתוך UNITCHECK, CHECK ו-END, בלוקים שנכתבו מאוחר יותר רצים מוקדם יותר.
בלוקי END בפירוט#
END הוא ה-phaser של הניקוי. הוא רץ LIFO כך שהפירוק מתפרק בסדר ההפוך לזה של ההתקנה, באופן שמשאבים מקוננים מצפים לו.
בתוך בלוק END, $? מחזיק את הערך שהתוכנית עומדת להעביר ל-exit. תוכל לקרוא אותו כדי ללמוד כיצד התוכנית מסתיימת, ותוכל להשים לתוכו כדי לשנות את קוד היציאה:
END {
$? = 0 if $? == 42; # rewrite one exit code on the way out
}
היזהר מדריסה של $? בטעות. הרצת כל דבר באמצעות system בתוך בלוק END דורסת את $? עם סטטוס תהליך-הבן, שאז הופך לקוד היציאה של התוכנית אלא אם תשמור ותשחזר אותו.
בלוקי END אינם רצים בכמה מקרים:
תחת המתג
-c(בדיקת תחביר בהידור-בלבד). הקוד הראשי אינו רץ, וגם בלוקיENDאינם רצים.כאשר ההידור נכשל. תוכנית שאינה מתהדרת לעולם אינה מגיעה לנקודה שבה בלוקי
ENDהיו נורים.כאשר התהליך מחליף את עצמו באמצעות
exec. התמונה איננה; לא נותר דבר שירוץ את הבלוק.כאשר התהליך נהרג על ידי אות קטלני שאינך תופס. תפוס את האות בעצמך (באמצעות
%SIG) אם דרוש לך שהניקוי ירוץ.
בלוק END שנוצר בתוך eval של מחרוזת אינו רץ כש-eval זה מסתיים. הוא נרשם כמו כל בלוק END אחר של החבילה שלו ורץ בסדר LIFO רגע לפני שהמפרש יוצא.
${^GLOBAL_PHASE} והשלבים#
המשתנה לקריאה בלבד ${^GLOBAL_PHASE} נוקב בשם השלב הנוכחי של המפרש. ערכיו מאפשרים לקוד שרץ ביותר משלב אחד לגלות היכן הוא נמצא. במהלך בלוק CHECK הוא קורא CHECK; במהלך INIT, INIT; במהלך קוד רגיל בזמן ריצה, RUN; במהלך בלוק END, END. ראה ${^GLOBAL_PHASE} לרשימה המלאה של הערכים ולציר הזמן של השלבים.
בלוקי defer#
בלוק defer הוא בן-הדוד בקנה-מידה קטן של END. הוא רץ כשתחום הבלוק העוטף יוצא, מכל סיבה שהיא, ולא כשהתוכנית כולה יוצאת. פנה אל defer כדי לזווג רכישה עם שחרור בתוך שגרה או לולאה; פנה אל END עבור פירוק כלל-תהליכי שחייב לקרות פעם אחת, ממש בסוף.
ראו גם#
BEGINanduse-useהוא טעינה-וייבוא עטופים ב-BEGIN; הסיבה הנפוצה ביותר להתעניין בתזמון שלב ההידור.${^GLOBAL_PHASE}- קרא את השלב הנוכחי מתוך כל בלוק כדי ללמוד היכן אתה נמצא.defer- ניקוי בתחום הבלוק, הכלי הנכון כשהטווח הכלל-תוכניתי שלENDרחב מדי.$?- סטטוס היציאה שבלוקENDיכול לקרוא ולכתוב מחדש.%SIG- תפוס את האותות שאחרת היו עוקפים את בלוקי ה-ENDכליל.exit- מה שבלוקיENDרצים רגע לפניו; הערך שהוא נושא הוא ה-$?שבלוקENDרואה.