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

rename#

משנה את שמו של קובץ.

rename מבקש ממערכת ההפעלה לקשור מחדש את הנתיב OLDNAME ל־NEWNAME. במערכת קבצים POSIX זוהי פעולת ספרייה אטומית יחידה: לאחר שהקריאה חוזרת בהצלחה, NEWNAME מתייחס לקובץ שהיה נגיש כ־OLDNAME, ו־OLDNAME כבר אינו קיים. זהו אותו פרימיטיב שפקודת shell mv פונה אליו כאשר המקור והיעד חיים באותה מערכת קבצים.

תקציר#

rename OLDNAME, NEWNAME

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

1 בהצלחה, 0 בכישלון כש־$! מוגדר ל־errno שקריאת המערכת rename(2) הבסיסית החזירה. תמיד בדוק את ערך ההחזרה - כישלון שקט כאן משאיר את מערכת הקבצים במצב ששאר התוכנית אינה מצפה לו:

rename $tmp, $final
    or die "rename $tmp -> $final failed: $!";

אטומיות וכלל אותה־מערכת־קבצים#

rename הוא אטומי רק כאשר OLDNAME ו־NEWNAME נפתרים לאותה מערכת קבצים. בתוך מערכת הקבצים הזו, שום מצב ביניים אינו נראה: תהליך אחר הקורא את הספרייה רואה או את השם הישן או את השם החדש, אף פעם לא את שניהם ואף פעם לא אף אחד. זו הסיבה שהאידיום הקנוני ״כתיבת קובץ בבטחה״ כותב לקובץ זמני באותה ספרייה ולאחר מכן עושה rename אליו למקומו:

open my $fh, ">", "$path.tmp" or die "open: $!";
print $fh $data;
close $fh or die "close: $!";
rename "$path.tmp", $path
    or die "rename: $!";

בין מערכות קבצים הקריאה נכשלת עם EXDEV ("Invalid cross-device link") ושום דבר אינו זז. פקודת mv של המערכת מטשטשת זאת בכך שהיא נופלת חזרה לעתק־ואז־unlink; rename אינו עושה כך. למעבר נייד בין־התקנים, השתמש ב־move מ־File::Copy, שמבצע בעצמו את הנפילה־חזרה copy-and-unlink.

דוגמאות#

שינוי שם בתוך ספרייה:

rename "draft.txt", "final.txt"
    or die "rename: $!";

החלפת קובץ אטומית - כתוב לקובץ זמני אחאי, ואז עשה rename:

my $tmp = "$path.$$.tmp";
open my $fh, ">", $tmp or die "open $tmp: $!";
print $fh $content;
close  $fh             or die "close $tmp: $!";
rename $tmp, $path     or die "rename $tmp -> $path: $!";

זיהוי מקרה בין־התקנים ונפילה חזרה ל־File::Copy::move:

use Errno qw(EXDEV);
use File::Copy qw(move);

unless (rename $src, $dst) {
    if ($! == EXDEV) {
        move($src, $dst) or die "move: $!";
    }
    else {
        die "rename: $!";
    }
}

שינוי שם של כל .log בספרייה ל־.log.old:

for my $f (glob "*.log") {
    rename $f, "$f.old"
        or warn "rename $f: $!";
}

מקרי קצה#

  • NEWNAME כבר קיים: נדרס בשקט. rename אינו שואל, מזהיר או מסרב. אם היעד הוא קובץ רגיל הוא מבוטל כחלק מהפעולה; אם הוא ספרייה הקריאה נכשלת עם ENOTDIR או EISDIR בהתאם אם OLDNAME הוא ספרייה. כדי לקבל סמנטיקת ״סרב לדרוס״, בדוק עם -e תחילה (וקבל שהבדיקה היא race - אין renameat2(RENAME_NOREPLACE) נייד שחשוף דרך rename עצמו).

  • בין־מערכות־קבצים (EXDEV): נכשל, משאיר את שני הנתיבים שלמים. השתמש ב־File::Copy::move כאשר היעד עשוי להיות על mount אחר.

  • מטפלי קובץ פתוחים: ב־Linux (וב־POSIX באופן כללי) שינוי שם של קובץ שכרגע פתוח הוא תקין. מטפל הקובץ הפתוח ממשיך להצביע על אותו inode; קריאות וכתיבות עוקבות דרכו ממשיכות לעבוד. פתיחות חדשות משתמשות בשם החדש.

  • ספריות: שינוי שם של ספרייה מותר, אך רק כאשר NEWNAME אינו קיים או הוא ספרייה ריקה. הזזת ספרייה לתת־ספרייה של עצמה נכשלת עם EINVAL.

  • הרשאות: rename זקוק להרשאת כתיבה + הרצה על שתי ספריות־ההורה של המקור והיעד, לא על הקובץ עצמו. כישלון מופיע כ־EACCES או EPERM.

  • ספריות sticky־bit (/tmp): אם להורה היעד מוגדר sticky bit, עליך להיות בעלים של או קובץ המקור או הורה היעד; אחרת הקריאה נכשלת עם EPERM גם כאשר הספרייה ניתנת לכתיבה לכולם.

  • Symlinks: rename פועל על הקישור עצמו, לעולם לא על היעד שלו. rename "link", "other" משנה את שם ה־symlink; הקובץ אליו הוא מצביע אינו נוגע בו.

  • slash גורר ב־OLDNAME: קרנלים רבים דוחים זאת עם ENOTDIR כאשר המקור אינו ספרייה. אל תסתמך על כך שהתנהגות slash־גורר תהיה ניידת.

  • rename של אותו נתיב: rename $p, $p מצליח ואינו עושה דבר (התנהגות הנדרשת על־ידי POSIX) כל עוד $p קיים.

הבדלים מהמקור#

תאימות מלאה עם Perl 5.42 המקורי.

ראו גם#

  • unlink - מסיר קובץ לפי שם; המקבילה לפירוק של יצירת אחד עם rename למקום

  • link - יוצר שם קישור־קשיח נוסף לקובץ קיים מבלי להסיר את המקור

  • symlink - יוצר קישור סימבולי; שים לב ש־rename על symlink מזיז את הקישור, לא את היעד שלו

  • File::Copy - move ו־copy ניידים שמטפלים במקרה בין־מערכות־קבצים (EXDEV) ש־rename אינו יכול

  • $! - errno לאחר rename כושל; השווה מול קבועים מ־Errno (EXDEV, EACCES, ENOENT, …)