exec#
לנטוש את התוכנית הזו ולהריץ אחרת באותו תהליך.
exec מחליף את דמות התהליך הנוכחית בתוכנית אחרת. הוא אינו חוזר בהצלחה - הביצוע ממשיך בתוכנית החדשה, עם אותו process id, מטפלי קובץ פתוחים בכפוף לדגל close-on-exec, וכל סביבה שהייתה בתוקף. הוא מחזיר שקר (ומגדיר את $!) רק כאשר המטרה לא יכלה להיות מופעלת וגם היא בוצעה ישירות במקום דרך ה־shell. יש לפנות ל־system כאשר רוצים שהבן ירוץ והבקרה תחזור.
תקציר#
exec LIST
exec PROGRAM LIST
exec { PROGRAM } LIST
מה מקבלים בחזרה#
כלום, בהצלחה - התהליך הוחלף ומפרש Perl הזה נעלם. בכישלון exec מחזיר שקר ומשאיר את הסיבה ב־$!. כישלון ניתן לזיהוי רק במסלול ה־direct-exec; כאשר הארגומנט מועבר ל־shell, תוכנית חסרה מדווחת על־ידי ה־shell עצמו ו־exec עדיין נחשב כאילו ״הצליח״ בהפעלת /bin/sh.
exec '/usr/bin/vi', $file
or die "couldn't exec vi: $!";
מכיוון ש־exec בדרך כלל אינו חוזר, Perl פולט אזהרה תחת use warnings אם הוא מופיע בהקשר void ואחריו הוראה אחרת שאינה die, warn, או exit. יש לטפל בכישלון inline עם or die, או לעטוף את הקריאה בבלוק כדי להשתיק את האזהרה:
exec('foo') or die "couldn't exec foo: $!";
{ exec('foo') }; die "couldn't exec foo: $!";
Shell או לא־shell - הכלל של מחרוזת יחידה#
exec בוחר בין שני מנגנוני הפעלה על בסיס הצורה של LIST:
שני ארגומנטים או יותר ←
execקורא ל־execvp(3)ישירות עם הרשימה. אין shell מעורב, אין פירוש מטה־תווים, אין פיצול מילים.ארגומנט בודד בדיוק ←
execסורק את המחרוזת היחידה הזו לאיתור מטה־תווים של shell. אם נמצאים כאלה, המחרוזת כולה מועברת ל־/bin/sh -cלניתוח. אם אין, המחרוזת מפוצלת על רווחים ומועברת ל־execvpישירות.
הכלל חשוב הן לנכונות והן לאבטחה. מחרוזת חד־ארגומנטית שמכילה רווח אך ללא מטה־תווים מפוצלת למילים ומבוצעת ישירות - וזה לרוב מה שרוצים. מחרוזת חד־ארגומנטית שמכילה |, >, $, ;, תווי glob, או מרכאות עוברת דרך ה־shell, וה־shell ירחיב בשמחה תווים כלליים, יפרש משתנים, ויפצל על $IFS.
exec '/bin/echo', 'Your arguments are:', @ARGV; # direct execvp
exec "sort $outfile | uniq"; # pipeline → shell
exec "ls /tmp"; # no metachars → split + execvp
כדי לאלץ את מסלול ה־no-shell גם עם פקודה לוגית בודדת, יש להשתמש בצורת ה־indirect-object שמתוארת להלן.
צורת indirect-object - להגדיר את argv[0], לדלג על ה־shell#
הצבת בלוק או סקלר לפני LIST (ללא פסיק) אומרת ל־exec שני דברים בו זמנית: להריץ את התוכנית הזו, ולהעביר את אותם ארגומנטים כ־argv. האלמנט הראשון של LIST הופך ל־argv[0] - השם שהבן רואה לעצמו - שאינו חייב להתאים לנתיב התוכנית.
exec { '/bin/echo' } 'echo', 'hi'; # argv[0] is "echo"
my $shell = '/bin/csh';
exec $shell '-sh'; # login-shell convention: argv[0] = "-sh"
exec { '/bin/csh' } '-sh'; # same, more direct
צורת ה־indirect-object תמיד מתייחסת ל־LIST כרשימה רבת־ערכים, גם אם יש בה רק אלמנט אחד. זה משבית את סריקת המטה־תווים לחלוטין:
my @args = ('echo surprise');
exec @args; # one element → shell scan → /bin/sh -c 'echo surprise'
exec { $args[0] } @args; # always direct execvp, "echo surprise" is argv[0]
בצורה השנייה אין תוכנית על הדיסק שנקראת באופן מילולי echo surprise, כך ש־execvp נכשל, exec מחזיר שקר, ו־$! מחזיק את הסיבה. זוהי ההתנהגות הבטוחה: הקורא מקבל את ההזדמנות להחליט מה לעשות, במקום שה־shell ירוץ משהו לא־צפוי.
מצב גלובלי שהוא נוגע בו#
$!- מוגדר בכישלון ל־errnoמהקריאה הבסיסיתexecvp(3). משמעותי רק כאשרexecאכן חוזר.$?- נשאר ללא שינוי על־ידיexecעצמו, אך ניסיון כושל בתיווך shell עשוי להשאיר את סטטוס היציאה של ה־shell נראה ל־systemעוטף.מטפלי הקובץ הפתוחים של התהליך, הסביבה (
%ENV), ספריית העבודה הנוכחית,umask, סידורי אותות, ומגבלות משאבים - כולם עוברים לתוכנית המחליפה -execמחליף את התוכנית, לא את התהליך.
דוגמאות#
להריץ תוכנית עם רשימת ארגומנטים מפורשת, execvp ישיר:
exec '/usr/bin/git', 'status', '--short'
or die "exec git: $!";
להעביר שורת פקודה ל־shell במכוון, באמצעות מטה־תווים:
exec "find . -name '*.tmp' -print0 | xargs -0 rm"
or die "exec pipeline: $!";
להגדיר argv[0] מובחן כך ששורת ה־ps של הבן מזהה את תפקידו:
exec { '/usr/local/bin/worker' } 'worker[queue=email]', @flags
or die "exec worker: $!";
Fork-and-exec - התבנית הקלאסית להפעלת בן מבלי להחליף את עצמך:
my $pid = fork // die "fork: $!";
if ($pid == 0) {
exec '/usr/bin/gzip', '-9', $file
or die "exec gzip: $!";
}
waitpid $pid, 0;
להבטיח את מסלול ה־no-shell כאשר רשימת הארגומנטים הורכבה מנתונים:
my @cmd = build_command(); # may contain one element or many
exec { $cmd[0] } @cmd
or die "exec $cmd[0]: $!";
מקרי קצה#
לעולם לא חוזר בהצלחה. כל קוד אחרי
execחשוף ניתן להגיע אליו רק בכישלון. תחתuse warnings, הוראה עוקבת שאינהdie,warn, אוexitמפעילה אזהרה - רמז של Perl שכנראה התכוונת ל־system.מלכודת ארגומנט בודד.
exec $cmdכאשר$cmdהגיע מקלט משתמש הוא וקטור הזרקת shell אם$cmdמכיל מטה־תווים. יש להשתמש בצורת ה־indirect-object או להעביר רשימה מפוצלת מראש.אגירת פלט. Perl מנסה לפלוט את התוכן של מטפלים שנפתחו לפלט לפני העברת הבקרה לתוכנית החדשה, אך זה לא מובטח בכל פלטפורמה. אם יש פלט ממתין על מטפל לא־מפוילט, יש להגדיר את
$|על המטפל הנבחר - או לקרוא למתודהautoflushעל כלIO::Handle- לפניexec.בלוקי
ENDו־DESTROY.execאינו מריץ בלוקיEND, והוא אינו מפעילDESTROYעל העצמים שלך. דמות התהליך מוחלפת; ניקוי ברמת Perl לעולם לא קורה. כל מה שדורש סגירה נקייה (קבצים זמניים, הסרת lockfile, פליטת לוגים אגורים) חייב לקרות לפניexec.Close-on-exec. מטפלי קובץ עם הדגל
FD_CLOEXECמוגדר נסגרים על־ידי הקרנל במהלךexecvp; אחרים נשארים פתוחים. יש לשלוט בזה עםfcntlאו עם סף מטפל הקובץ של המערכת$^F.כישלון בתיווך shell אינו נראה. כאשר
execמנותב דרך/bin/sh -c, תוכנית מטרה חסרה מדווחת על־ידי ה־shell ו־execעצמו כבר הוחלף על־ידי/bin/sh. קוד ה־Perl שלך לא יראה החזרת כישלון.רשימת ארג בודד ללא מטה־תווים עדיין מפוצלת על רווחים ומועברת ל־
execvp- אזexec "ls /tmp"הוא exec ישיר של/bin/lsעם ארגומנט אחד, לא קריאת shell.LISTריק היא שגיאת זמן ריצה; אין תוכנית להריץ.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
system- להריץ תוכנית ולחכות לה; מה שכמעט בוודאות רצית אםexecנמצא בראש הקובץfork- לשייך עםexecבבן כדי להפעיל תוכנית מבלי להחליף את האבwait- לאסוף בן שנוצר על־ידיfork+execdie- לסיים את התוכנית הנוכחית עם שגיאה; השותף המקובל שלexec ... or die$!- סיבת השגיאה שמוגדרת כאשר direct-exec נכשל$|- דגל autoflush; יש להגדיר לפניexecכדי שפלט אגור לא יאבד כאשר דמות התהליך מוחלפת