מטפלי קובץ קבצים ספריות

chdir#

משנה את ספריית העבודה הנוכחית של התהליך.

chdir הוא העטיפה של Perl סביב קריאות המערכת chdir(2) ו־ - היכן שהקרנל תומך בכך - fchdir(2). הוא משנה את הספרייה שכנגדה נתיבים יחסיים מקבלים פתרון עבור התהליך הנוכחי, ובהצלחה משמר את השינוי הזה לכל פעולת קובץ עוקבת בתוכנית.

תקציר#

chdir EXPR
chdir FILEHANDLE
chdir DIRHANDLE
chdir

מה מקבלים בחזרה#

1 בהצלחה, 0 בכישלון. בכישלון $! נקבע ל־errno הבסיסי כדי שניתן יהיה להבדיל בין ״אין ספרייה כזו״ (ENOENT), ״הרשאה נדחתה״ (EACCES), לבין ״לא ספרייה״ (ENOTDIR):

chdir $path
    or die "chdir $path failed: $!";

ההחזרה היא תמיד בוליאני פשוט - אין ערך נפרד של ״הוחלף לשום מקום כי לא ניתן ארגומנט״. הצורה ללא־ארגומנט מצליחה או על־ידי החלפה לספריית הבית או נכשלת כמו כל קריאה אחרת.

מצב גלובלי שהוא נוגע בו#

  • %ENV - נקרא כאשר chdir נקרא ללא ארגומנט. סדר החיפוש הוא $ENV{HOME} תחילה, אחר־כך $ENV{LOGDIR}. אם אף אחד מהם לא מוגדר, chdir לא עושה דבר ומחזיר 0.

  • $! - נקבע בכישלון ל־errno שדווח על־ידי הקרנל.

chdir אינו מעדכן את $ENV{PWD}. מעטפות (shells) מתחזקות את PWD בעצמן; תוכנית Perl שאכפת לה מ־PWD עבור תהליכי־ילד צריכה להציב ל־$ENV{PWD} לאחר chdir מוצלח.

צורות ארגומנט#

  • chdir EXPR - EXPR מומר למחרוזת ומועבר ל־chdir(2) כשם נתיב. נתיבים יחסיים מקבלים פתרון כנגד ספריית העבודה הנוכחית ברגע הקריאה.

  • chdir FILEHANDLE ו־chdir DIRHANDLE - בלינוקס (וכל מערכת עם fchdir(2)) הקרנל משנה ספרייה לזו שאליה המטפל מתייחס. FILEHANDLE כאן משמעו מטפל קובץ שנפתח על ספרייה, או מטפל ספרייה מ־opendir. שימוש בצורת המטפל נמנע ממירוץ TOCTOU בין פתרון שם נתיב לבין החלפה אליו.

  • chdir (ללא ארגומנט) - מחליף ל־$ENV{HOME}, ונופל חזרה ל־$ENV{LOGDIR} אם HOME לא מוגדר. אם שניהם לא מוגדרים הקריאה נכשלת ו־$! נשאר ללא שינוי; יש לבדוק את ערך ההחזרה, לא את $!.

דוגמאות#

החלפה לספרייה ויציאה אם היא לא קיימת:

chdir "/var/log"
    or die "cannot enter /var/log: $!";

הרצת בלוק עבודה בספרייה אחרת וחזרה למקום בו התחלתם:

use Cwd qw(getcwd);
my $origin = getcwd;
chdir $work_dir or die "chdir $work_dir: $!";
# ... do work relative to $work_dir ...
chdir $origin  or die "chdir $origin: $!";

החלפה דרך מטפל ספרייה (ללא תנאי־מירוץ במערכות עם fchdir(2)):

opendir my $dh, $path or die "opendir $path: $!";
chdir $dh              or die "fchdir $path: $!";

לחזור הביתה:

chdir or die "no HOME/LOGDIR in environment";

בדיקה של רשימת מועמדים, עצירה בראשון שעובד:

for my $d ("/srv/app", "/opt/app", "/usr/local/app") {
    last if chdir $d;
}

מקרי קצה#

  • Empty string or undef: chdir "" and chdir undef both fail and set $! to ENOENT. They do not fall back to $HOME

    • the no-argument form is triggered by absence of an argument, not by an argument that happens to be empty.

  • נתיבים יחסיים ב־thread עם cwd משלו: ספריית העבודה הנוכחית של הקרנל היא לכל־תהליך, לא לכל־thread, בלינוקס. שני thread-ים הקוראים ל־chdir בו־זמנית מתחרים.

  • symlink לספרייה: chdir עוקב אחרי symlink-ים, ונוחת ביעד. יש להשתמש ב־readlink או Cwd::abs_path קודם אם צריך לדעת את היעד האמיתי.

  • ספרייה שנמחקה: אם הספרייה הוסרה לאחר שנכנסתם אליה, פעולות יחסיות מבפנים נכשלות עם ENOENT ורוב המעטפות מציגות את הנתיב כ־(deleted). עדיין ניתן לבצע chdir החוצה בעזרת נתיב מוחלט.

  • צורת המטפל במערכות ללא fchdir(2): מעלה חריגה. לינוקס תמיד תומך בכך; ההגבלה היא דאגת ניידות עבור קוד שרץ גם על מערכות ישנות יותר.

  • קלט לא־מהימן: העברת נתיב שסופק על־ידי משתמש ל־chdir ללא ולידציה מאפשרת לקורא להפנות מחדש את כל פעולות הנתיב היחסי הבאות בתוכנית. יש לקנן (canonicalise) באמצעות Cwd::abs_path ולבדוק כנגד allow-list לפני הקריאה אם הקלט חוצה גבול אמון.

  • לאחר fork: הילד יורש את ה־cwd של ההורה. chdir בילד אינו משפיע על ההורה.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • mkdir - יצירת ספרייה; מצמדים עם chdir כשבונים סביבת עבודה ונכנסים אליה

  • rmdir - מחיקת ספרייה; לא לבצע rmdir על ה־cwd, יש לבצע chdir החוצה תחילה

  • opendir - פתיחת מטפל ספרייה שניתן להשתמש בו כארגומנט ל־chdir עבור שינויי ספרייה נטולי תנאי־מירוץ

  • chroot - בידוד חזק יותר מ־chdir; משנה את שורש מערכת הקבצים עבור התהליך, לא רק את ה־cwd

  • $ENV{HOME} - מתייעצים בו על־ידי צורת ללא־הארגומנט של chdir לפני נפילה חזרה ל־$ENV{LOGDIR}

  • $! - נושא את ה־errno כאשר chdir מחזיר 0