מספר דרכים לשרשור פקודות ב- Bash:
- הרצת סקריפט שמכיל את הפקודות אחת אחר השניה (כל פקודה בשורה נפרדת).
– כל הפקודות ירוצו באותו ה- shell (גם אם הן מצליחות וגם אם לא) - הרצת הפקודה: cmd1;cmd2;cmd3
– הפקודות מופרדות ע"י ';' ורצות אחת אחר השניה
– כל פקודה ממתינה לסיום הפקודה הקודמת כשכל פקודה רצה ב- shell אחר (קראו את התגובות) - הרצת הפקודה: (cmd1;cmd2;cmd3)
– כמו 2 רק שכל הפקודות רצות באותו ה- shell ברקע (קראו את התגובות) - הרצת הפקודה: cmd1 && cmd2 && cmd3
-בהתחלה תרוץ הפקודה cmd1, אם תצליח תרוץ הפקודה cmd2 ואם לא תצליח לא תורץ אף פקודה נוספת.
– '&&' שונה מ- ';' בכך שבשימוש ב- ';' הפקודות תמיד רצות אחת אחר השנייה (ללא תלות בתוצאות ההרצה הקודמת) - ניתן להשתמש בסקריפט ולבדוק אם כל פקודה הצליחה או לא
– ניתן לדעת אם פקודה הצליחה או לא ע"י בדיקת הערך של '?$'. אם '?$' מחזיר 0 הפקודה הצליחה ואם מחזיר כל ערך אחר הפקודה נכשלה.
– ניתן לבצע בדיקות נוספות ומתוחכמות יותר ע"י שימוש ב- || (שמשמעותו 'או') או ב- && ('וגם') - שימוש ב- & (הרצה ברקע): cmd1 & cmd2 & cmd3
כל הפקודות ירוצו במקביל אך זאת שתסיים ראשונה תציג את הפלט ראשונה (אם יש פלט)
כמעט בטוח שיש דרכים נוספות ואשמח להתעדכן (גם בטעויות שרשמתי…).
התפקיד של '|' די דומה ל־';' בהקשר של הרצת פקודות, אבל שכחת לציין אותו.
1 ו־2 הם אותו דבר. בשניהם הפקודה רצה באותו ה־shell . כמובן שהרצה של פקודה חיצונית תיצור תהליך נפרד. אולם אם הפקודה היא פונקציה או פקודה פנימית של bash היא לא תיצור תהליך חדש.
דווקא | כופה יצירה של תהליך בן: כל התהליכים שבצנרת חייבים להיות בנים מכיוון שצריכים לשנות להם את ה־fie descriptors . לכן, לדוגמה:
a=1
true | a=2
echo $a
ידפיס 1 ולא 2 .
&& מיועד להריץ פקודה רק אם פקודה אחרת הצליחה.
|| מיועד להריץ פקודה רק אם פקודה אחרת נכשלה.
כלומר:
cmd1 && cmd2
cmd3 || cmd4
מקביל ל:
if cmd1; then cmd2; fi
if ! cmd3; then cmd4; fi
צפריר, תודה (השכלתי מהתשובה).
בקשר ל- '|': זה קצת מבלבל כי '|' משתמש גם כ- pipe, כלומר שהפלט של פקודה אחת משמש כקלט של פקודה אחרת (כך שנראה שהתשובה של תומר איננה נכונה)
כמוכן, ה־| בכלל לא מתיימר להבטיח הרצה של פקודות אחת אחרי השניה. להפך: פקודות שמורות לרוץ במקביל בצנרת.
$ (echo 1: before sleep; sleep 2; echo 1: after sleep)>/dev/tty | (echo 2: before sleep; sleep 2; echo 2: after sleep)
2: before sleep
1: before sleep
1: after sleep
2: after sleep
לעומת פקודות שמופרדות ב';' (כלומר מורצות אחת אחרי השניה):
$ (echo 1: before sleep; sleep 2; echo 1: after sleep)>/dev/tty ; (echo 2: before sleep; sleep 2; echo 2: after sleep)
1: before sleep
1: after sleep
2: before sleep
2: after sleep
נדיר מאד לתקן את צפריר…
1 ו-2 הם לא אותו דבר; 1 ו-3 הם אותו דבר. בשלושתם, כל הפקודות מתבצעות באותו ה-shell, ובאף אחד מהם הן לא ברקע; ההבדל בין 2 לבין האחרים הוא שב-1 ו-3, ה-shell הזה הוא shell מיוחד שנפתח לצורך הרצת הפקודה, ואילו ב-2 הפקודות מורצות בתוך ה-shell ה"רגיל", האינטראקטיבי.
[code]
$ a=1;echo $a
1
$ (a=2; echo $a); echo $a;
2
1
$ cat temp.sh
a=3
echo $a
$ sh temp.sh ; echo $a
3
1
[/code]
בפירוט יותר — הרצת פקודות בתוך סוגריים, כמו בתוך סקריפט, יוצרות סביבה חדשה, ששינויי משתנים בה לא משפיעים בחוץ; בלי הסוגריים, הם כן משפיעים. הרצה ברקע לא קשורה לסוגריים.
אגב, אפקט שימושי במיוחד של ה"אי-השפעה" הזו הוא לגבי שינויי תיקיה; אם בתוך רצף הפקודות שבסוגריים נכללת גם cd, היא תשפיע על הפקודות שלאחריה, אולם בסוף הרצף תיקיית-העבודה לא תשתנה.
אני מודע לנושא ה-redirection, רק הכנתי לך פתיחה למאמר הבא בסדרה. 🙂
שי
תודה על התגובה. הדוגמא שסיפקת מאוד ברורה (הייתה לי הרגשה, כשכתבתי את הפוסט, שאחכים מהתגובות, וכך באמת קרה… 🙂 )
בכל מקרה: הכנסתי בפוסט הפניות לתגובות.
מאמר מעולה, נתת לי הרבה השראה לפוסט בנושא
תודה רבה, תייש.