system#
הרצת תוכנית נפרדת והמתנה לסיומה.
system עושה בדיוק את אותו הדבר כמו exec, אלא ש־fork מבוצע תחילה ותהליך האב ממתין שהילד יצא. ערך ההחזרה הוא סטטוס ההמתנה של הילד, לא הפלט שלו - אם רוצים את הפלט של פקודה, יש להשתמש ב־backticks או ב־qx// במקום.
תקציר#
system LIST
system PROGRAM LIST
צורת PROGRAM LIST העקיפה משקרת לתוכנית לגבי שמה־שלה: PROGRAM הוא הקובץ שמורץ בפועל, בעוד שהאלמנט הראשון של LIST הופך ל־argv[0] בתוך הילד. אותו טריק כמו ב־exec.
מה מקבלים בחזרה#
ערך ההחזרה הוא מילת הסטטוס הגולמית מקריאת wait שבבסיס - אותו ערך שנוחת ב־$? לאחר הקריאה. כדי לשחזר את קוד היציאה האמיתי של התוכנית, יש להזיז ימינה בשמונה:
system("make", "install");
my $exit = $? >> 8;
-1 משמעו שלא ניתן היה להפעיל את התוכנית כלל (או ש־wait עצמו נכשל); יש לבחון את $! לסיבה. החזרה של 0 משמעה שהילד יצא בהצלחה עם סטטוס 0.
הרצה דרך shell מול הרצה ישירה#
עיבוד הארגומנטים תלוי בצורת LIST, ומשקף את exec בדיוק:
יותר מארגומנט אחד (או מערך עם יותר מאלמנט אחד): האלמנט הראשון הוא התוכנית, השאר הם הארגומנטים שלה. ה־shell אינו מופעל;
execvpמריץ את התוכנית ישירות. זוהי הצורה הבטוחה.בדיוק ארגומנט סקלרי אחד: Perl סורק אותו לחיפוש מטה־תווים של shell. אם נוכחים כאלה, כל המחרוזת מועברת ל־
/bin/sh -cלפענוח. אם אין כאלה, Perl מפצל לפי רווחים וקורא ל־execvpישירות - חוסך shell.
מומלץ להעדיף את צורת הרשימה בכל פעם שהארגומנטים מגיעים מקלט לא־מהימן או מכילים תווים שה־shell היה מפרש:
system("ls", "-l", $dir); # safe, no shell
system("ls -l $dir"); # shell parses $dir - hazardous
אותות מול יציאה#
מילת הסטטוס משלבת שלושה פיסות מידע: מספר אות (אם הילד מת על אות), דגל core-dump, וערך היציאה. יש לפענח אותה במפורש כאשר נדרש להבחין ביניהם:
system(@cmd);
if ($? == -1) {
print "failed to execute: $!\n";
}
elsif ($? & 127) {
printf "child died with signal %d, %s coredump\n",
($? & 127), ($? & 128) ? 'with' : 'without';
}
else {
printf "child exited with value %d\n", $? >> 8;
}
לפענוח מובנה, יש לבחון את ${^CHILD_ERROR_NATIVE} - מילת הסטטוס המקומית המלאה - עם המאקרואים WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG מ־POSIX:
use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG);
system(@cmd);
my $n = ${^CHILD_ERROR_NATIVE};
if (WIFEXITED($n)) { my $code = WEXITSTATUS($n); ... }
if (WIFSIGNALED($n)) { my $sig = WTERMSIG($n); ... }
SIGINT ו־SIGQUIT מתעלמים מהם באב בעוד הילד רץ. אם רוצים שהתוכנית שלכם תמות כשהמשתמש מקיש Ctrl-C במהלך system, יש לבדוק את סטטוס ההחזרה בעצמכם ולזרוק מחדש:
system(@cmd);
kill 'INT', $$ if ($? & 127) == 2; # re-raise SIGINT on self
שטיפת stdio#
לפני ה־fork, Perl מנסה לשטוף כל מטפל קובץ שנפתח לפלט. זה מונע משורות בתוך buffer להשתכפל גם על־ידי האב וגם על־ידי הילד. השטיפה היא השתדלות מיטבית; בפלטפורמות שבהן היא לא־אמינה, יש להגדיר את $| ($AUTOFLUSH) או לקרוא למתודת autoflush מ־IO::Handle על מטפלים שאכפת מהם.
אידיום ה־die#
הבדיקה הקנונית - תמציתית, והיא משבצת את מילת הסטטוס בהודעה כך שהקורא עדיין יכול לפענח אותה:
my @args = ("command", "arg1", "arg2");
system(@args) == 0
or die "system @args failed: $?";
למודול שצריך לזרוק על כל system שנכשל (וחלקים רבים אחרים של Perl), יש להשתמש בפרגמה autodie.
דוגמאות#
הרצת פקודה והתעלמות מהתוצאה:
system("clear");
הרצת פקודה, מות בכישלון, ושמירת הסטטוס שלם לבדיקה במורד הזרם:
system("tar", "xf", $archive) == 0
or die "untar $archive failed: $?";
זיהוי האם תוכנית זמינה על־ידי הרצתה עם --version והשלכת הפלט שלה:
my $ok = system("which $prog >/dev/null 2>&1") == 0;
הרצה תחת shell ספציפי במפורש, עוקפת את היוריסטיקת זיהוי מטה־התווים של Perl:
system("/bin/bash", "-c", $pipeline);
שקר לגבי argv[0] באמצעות הצורה העקיפה - הקרנל מריץ את /usr/bin/vim, אך התוכנית רואה את שמה כ־editor:
system { "/usr/bin/vim" } "editor", $file;
מקרי קצה#
החזרה של -1: הילד מעולם לא נוצר, או ש־
waitעצמו נכשל. נבדל מ־״הילד יצא עם סטטוס 255״. יש לבדוק את$!לסיבה - בדרך כללENOENT(התוכנית לא נמצאה) אוEACCES(לא ניתן להרצה).ערך יציאה 255 מ־shell: כאשר נעשה שימוש בצורת ה־shell ו־
/bin/shאינו יכול למצוא את התוכנית, ה־shell עצמו יוצא עם127. תוכניות המחזירות255מ־Perl מגיעות בדרך כלל דרךdieבתוכנית Perl אחרת.אינטראקציה עם מטפל
SIGCHLD:systemמבצע fork פנימית וממתין. מטפל$SIG{CHLD}יכול לצפות או להתחרות עם המתנה זו; ראוperlipcעבור משמעת האיסוף.מוזרויות shell: כאשר צורת ה־shell מופעלת, קודי החזרה ודיווח השגיאות הם כל מה ש־
/bin/shאומר שהם - לא תמיד מה שהתוכנית שבבסיס החזירה. יש להשתמש בצורת הרשימה כדי להימנע מזה.לא ללכידת פלט:
systemכותב את פלט הילד ל־STDOUT/STDERRשנירשו. יש להשתמש ב־qx//או לפתוח pipe עםopenאם נדרש הפלט כמחרוזת.Windows: רק תחביר ה־
system PROGRAM LISTעוקף בצורה מהימנה את ה־shell. pperl היא Linux בלבד, לכן זה אינו חל כאן; מצוין למען ניידות.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
exec- להחליף את התהליך הנוכחי במקום לבצע fork ולהמתין; אותם כללי עיבוד ארגומנטים כמוsystemfork- ליצור תהליך־ילד בלי להריץ תוכנית חדשה;systemהואfork+exec+waitpidwaitpid- לאסוף ילד ספציפי, בשימוש כאשר מנהלים מספר ילדים ישירותqx- להריץ פקודה וללכוד את הפלט שלה כמחרוזת; מה שרוצים כאשר מיקוד ה־stdio שלsystemאינו מספיק$?- מילת סטטוס שגיאת ילד שמוגדרת על־ידיsystem, backticks,closeשל pipe, ו־wait$!- errno לאחר fork או exec שנכשל (ערך החזרה-1)${^CHILD_ERROR_NATIVE}- סטטוס המתנה מקומי מלא לפענוח עם המאקרואיםW*שלPOSIX