תהליכים

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 ולהמתין; אותם כללי עיבוד ארגומנטים כמו system

  • fork - ליצור תהליך־ילד בלי להריץ תוכנית חדשה; system הוא fork + exec + waitpid

  • waitpid - לאסוף ילד ספציפי, בשימוש כאשר מנהלים מספר ילדים ישירות

  • qx - להריץ פקודה וללכוד את הפלט שלה כמחרוזת; מה שרוצים כאשר מיקוד ה־stdio של system אינו מספיק

  • $? - מילת סטטוס שגיאת ילד שמוגדרת על־ידי system, backticks, close של pipe, ו־wait

  • $! - errno לאחר fork או exec שנכשל (ערך החזרה -1)

  • ${^CHILD_ERROR_NATIVE} - סטטוס המתנה מקומי מלא לפענוח עם המאקרואים W* של POSIX