תהליכים

qx//#

מריץ פקודת shell ולוכד את הפלט הסטנדרטי שלה.

qx// (ותאומו, צורת ה־backtick `STRING`) משבץ את STRING כמו מחרוזת בגרשיים כפולים, מעביר את התוצאה ל־shell של המערכת, ממתין לסיום הילד, ומחזיר את כל מה שהילד כתב ל־STDOUT. השגיאה הסטנדרטית נשארת ללא נגיעה - היא עדיין הולכת ל־STDERR של ההורה אלא אם תפנו אותה מחדש בתוך הפקודה. סטטוס היציאה של הילד נחות ב־$?.

תקציר#

my $out   = `date`;
my $out   = qx/date/;
my @lines = `ls -1 /etc`;
my $raw   = qx'echo $HOME';     # single-quote form: no Perl interpolation

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

רגיש להקשר:

  • הקשר סקלר - מחרוזת אחת המכילה את כל ה־STDOUT שנלכד, כולל כל שורה חדשה מוטמעת. undef אם ה־shell או הפקודה לא יכלו להתחיל.

  • הקשר רשימה - רשימת שורות, מפוצלת על־פי הערך הנוכחי של $/ (מפריד רשומות הקלט, ברירת מחדל שורה חדשה). רשימה ריקה אם ה־shell או הפקודה לא יכלו להתחיל.

סטטוס היציאה של הילד תמיד מושם ב־$?, בין אם הפלט הנלכד בשימוש ובין אם לא. ילד שלא כותב דבר ויוצא נקי מניב "" בהקשר סקלר, () בהקשר רשימה, ו־0 ב־$? - להבחין בין ״התחיל אך לא הפיק פלט״ לבין ״נכשל להתחיל״ דורש בדיקת $? כנגד -1 או $!.

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

  • $? - נקבע לסטטוס ההמתנה של הילד (בסגנון waitpid) בכל קריאה. -1 משמעו שהילד לא יכל להתחיל, ובמקרה זה $! מחזיק את הסיבה.

  • $! - נקבע כאשר ה־shell או הפקודה נכשלו להתחיל.

  • $/ - שולט בכיצד פלט בהקשר רשימה מפוצל לשורות.

  • $| - משפיע על האם פלט Perl ממוין ב־buffer מגיע לסביבת הילד לפני שהילד רץ; קבעו אותו על מטפלי פלט לפני qx// אם הסדר עם print קודם חשוב.

  • %ENV - מיוצא לילד כסביבה שלו.

  • ספריית העבודה הנוכחית, ה־umask, וה־file descriptors הפתוחים נרשים על־ידי הילד.

דוגמאות#

לכוד שורה בודדה:

my $today = `date`;
chomp $today;
print "today is $today\n";

לכידה שורה־אחר־שורה בהקשר רשימה:

my @users = `who`;
for my $line (@users) {
    my ($name) = split /\s+/, $line;
    print "logged in: $name\n";
}

שיבוץ מתרחש לפני שה־shell רואה את המחרוזת - משתני Perl מתפרשים תחילה, משתני shell מתפרשים בילד:

my $dir = "/etc";
my $out = `ls $dir`;            # Perl interpolates $dir
my $sh  = qx'echo $HOME';       # single-quote form: $HOME is shell's

הפניית STDERR אל הזרם הנלכד:

my $both = `gcc -v hello.c 2>&1`;

השליכו את STDERR, השאירו רק את STDOUT:

my $out = `find / -name core 2>/dev/null`;

בדקו את סטטוס היציאה:

my $out = `make test`;
if ($? == -1) {
    die "failed to execute make: $!";
}
elsif ($? & 127) {
    die sprintf "make died with signal %d", $? & 127;
}
elsif ($? >> 8) {
    die sprintf "make exited with status %d", $? >> 8;
}

מקרי קצה#

  • ללא מטה־תווים של shell: אם STRING לא מכיל מטה־תווים של shell, Perl עוקף את ה־shell ומבצע exec ישירות לפקודה - אותה אופטימיזציה כמו system. זה מהיר יותר ונמנע מרובד אחד של מירכוּב sh -c.

  • מתחם גרש בודד: qx'...' מדכא את שיבוץ הגרשיים הכפולים של Perl. ה־$var הליטרלי מועבר ל־shell, אשר עושה את ההתפרסות שלו. שימושי כאשר שמות המשתנים שייכים ל־shell, לא לתוכנית Perl שלכם.

  • מתחמים חלופיים: qx// מקבל כל מתחם מאוזן או לא־אלפאנומרי - qx{ls -l}, qx(ls -l), qx[ls -l], qx!ls -l!. בחרו אחד שלא מתנגש עם תחביר shell בפקודה שלכם. qXfooX הוא שגיאת תחביר - המתחם אסור שיהיה אלפאנומרי.

  • backticks אינם לוכדים STDERR: שום דבר שנכתב ל־STDERR של הילד לא מגיע למחרוזת המוחזרת. הפנו עם 2>&1 בתוך הפקודה אם רוצים בכך, או הריצו את הפקודה דרך open עם pipe לשליטה דקה יותר.

  • STDIN נירש: הילד קורא מכל מה שתהליך Perl החזיק ב־STDIN בעת הקריאה. כדי להאכיל את הילד בקלט ספציפי, השתמשו ב־open במצב pipe במקום.

  • סטטוס יציאה לעומת $!: $? שאינו אפס משמעו שהילד רץ ויצא עם שגיאה; $! משמעותי רק כאשר $? הוא -1 (ה־shell עצמו לא יכל להיות מופעל).

  • עלות זיכרון בהקשר סקלר: כל הפלט הנלכד מאוחסן ב־buffer בזיכרון לפני שמוחזר. עבור פקודות שמפיקות פלט גדול מאוד או חסר־גבולות, פתחו pipe עם open וקראו אותו באופן הדרגתי.

  • צורת פונקציה: qx// יכול להיכתב גם readpipe כאשר רוצים תחביר של קריאת פונקציה - שימושי כאשר מחרוזת הפקודה נבנית דינמית ועדיף להעביר אותה כסקלר במקום לשבץ אותה לתוך quote-like.

  • Buffering של פלט: Perl עושה flush למטפלי הפלט שלו ה־buffered לפני שהוא עושה fork לילד, אך הניידות משתנה. כאשר מקדימים qx// עם print ל־pipe או לקובץ, קבעו את $| על אותו מטפל כדי לוודא שהפלט שלכם מגיע ליעד לפני שהילד מתחיל.

  • בחירת shell: ה־shell בשימוש הוא /bin/sh ב־Unix. פקודות שמסתמכות על bash-isms זקוקות לעטיפת bash -c '...' מפורשת.

הבדלים מ־upstream#

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

ראו גם#

  • system - מריץ פקודת shell ללא לכידת פלט; בחרו בו כשמעניין אתכם רק סטטוס היציאה

  • exec - מחליף את התהליך הנוכחי בפקודה; ללא לכידה, ללא החזרה

  • readpipe - צורת הפונקציה־הנקובה־בשם של qx//; לוקח את הפקודה כארגומנט מחרוזת רגיל

  • open - מצב pipe (open my $fh, "-|", CMD) להזרמת פלט של ילד שורה אחר שורה במקום ל־buffer את הלכידה כולה

  • $? - סטטוס ההמתנה של הילד שנקבע על־ידי כל קריאת qx//; פענחו עם >> 8 עבור קוד יציאה ועם & 127 עבור אות סיום

  • $/ - שולט בכיצד qx// בהקשר רשימה מפצל פלט לשורות