היסטוריית גירסאות | ||
---|---|---|
גירסה 0.2 | 08-04-2007 | כתריאל טראום |
הומר לפורמאט ויקי | ||
גירסה 0.2 | 22-07-2002 | ברק בלוך |
גירסא ראשונה |
כל הזכויות שמורות © 2007, ברק בלוך, הרשות ניתנת להעתיק, לשנות ולהפיץ מדריך זה תחת התנאים של רשיון ה-GFDL
Linux הוא סימן מסחרי רשום של Linus Torvalds.
הכותב אינו נושא באחריות עבור שימוש ברעיונות, דוגמאות ומידע שבמדריך. השימוש הוא באחריות הקורא בלבד. המדריך עשוי להחיל טעויות ופרטים לא נכונים, שהשימוש בהם עשוי להיות מזיק למחשבך. למרות הסבירות הנמוכה, הכותב אינו לוקח כל אחריות
bash הוא למעשה מעטפת (shell), מאוד נוח ויעיל, שמפותח על ידי אנשי ה gnu והוא למעשה ה shell ברירת מחדל במערכות ה gnu/linux.
כמו כל shell נורמלי, הוא כולל במקביל למערכת חזקה של פקודות, גם שפת סקריפט קלה אך מאוד יעילה, שעליה בעיקר יידון מדריך זה.
חשוב לציין שהמדריך לא יכול ללמד את הכל, אבל אני נוטה להאמין שאחרי שתקרא את המדריך יהיה ביידך הידע הדרוש לבנית סקריפטים שימושיים.
אני ממליץ, לך להציץ בקודים של סקריפטים אחרים (ויש כאלה גם על מכונת הלינוקס) כדי לראות עוד דוגמאות ל”איך עושים את זה בעולם האמיתי”.
אני אתן 2 לינקים שיכולים לעזור, בטרם נמשיך: כל מה שתרצו לדעת על bash תמצאו במדריכים באתר ה GNU
מדריך קצר יותר, שאני באופן אישי השתמשתי בו בעיקר ללימוד. מדריך מאוד נוח וקל ללימוד (למתחילים) : BASH Programming - Introduction HOW-TO
קריאה נעימה. כמובן שאשמח לקבל הערות והארות.
כל מי שמתמש במערכות יוניקס , ובעיקר לינוקס, כדאי שיכיר את שפת הסקריפט של bash.
ידיעה של שפת מעטפת ברמה שימושית, יכולה לעזור לביצוע אוטומיזציה של תהליכים רבים, וזה מומלץ לכל משתמש יוניקס, ולמנהלי רשת על אחת כמה וכמה (אבל לא רק להם). כמו כן, חלק מהסקריפטים על מערכת הלינוקס, כתובים ב bash והיכרות עם הסביבה יכולה לסייע בהבנת אותם סקריפטים, מה שמבטיח הבנה טובה יותר של “איך המערכת עובדת”
כדי להמשתמש במדריך, כל מה שצריך זה קצת סבלנות, קצת רצון טוב.
הסקריפטים במדריך נבדקו על RedHat7.3 עם bash מגרסא: -2.05a-13
סביר להניח שרובם ככולם של הסקריפטים יעבדו גם בגרסאות bash אחרות.
כמו בכל מדריך ללימוד שפת תכנות / סקריפט - הסקריפט הראשון שלנו לא יעשה דבר פרט מפלט סטנדרטי של “hello world”.
ראשית אסביר בקצרה מהם השלבים לבניית סקריפט:
./script_name
ועכשיו לתוכנית הראשונה. יוצרים את קובץ הטקסט הבא:
#!/bin/bash echo Hello World
אחרי ששמרנו את השורות הנ”ל לתוך קובץ טקסט, אפשר לתת לו הרשאת ריצה, ולהריץ אותו. עכשיו נסביר מה יש לנו שם:
השורה הראשונה: #!/bin/bash מתחילה בתו #. לתו זה משמעות מיוחדת בשפה: הוא אומר שמדובר בהערה, כלומר למפרש הפקודות להתעלם מההמשך השורה.
אבל כשהוא בא כתו הראשון בקובץ הטקסט, יש לו משמעות שונה במקרה שלנו, היות והוא התו הראשון בקובץ הטקסט, יש לו משמעות, כשבא אחריו התו ”!” מה שיבוא אחריהם הוא המיקום של “מפרש הפקודות”.
במילים אחרות, כשמערכת הפעלה תתחיל להריץ את קובץ הטקסט, היא תבין לפי השורה הראשונה שמפרש הפקודות של הקובץ יושב ב /bin/bash .
אם במכונה שלך bash מותקן במקום אחר (נדיר) יש לשנות את השורה, בהתאם לשורה שבא bash מותקן. הרצת
which bash
תאמר לנו היכן ה bash מותקן (בעת הצורך).
השורה השניה כוללת את הפקודה “echo” שתפקידה הוא לשלוח למסך משתנים / מחרוזות, במקרה שלנו המחרוזת “hello world”. בטרם אתה ממשיך לקרוא את המדריך, אתה אמור להיות מסוגל להריץ את הסקריפט לבד, ולהבין אותו.
לשפות הסקריפט של המעטפת, יש יכולת שמזכירה מעט את “קבצי האצווה” של DOS או במילים אחרות, אפשר להריץ מתוכן ישירות קבצים אחרים.
למעשה, שפות ה shell הם קצבי האצווה של מערכות היוניקס !
אם נרצה למשל, לבצע סדר פקודות שונות שאחת תלויה בשניה, בצורה אוטומטית, נוכל לכתוב אותן בתוך קובץ סקריפט של BASH ולהריץ אותו. דוגמא מעשית מאירת עיניים היא הדוגמא הבאה:
#!/bin/bash export LC_ALL="he_IL" export LC_CTYPE=he export LC_MESSAGES=he export LANG="he" export LC_ALL="he_IL" locale | grep LC_ALL
בדוגמא הזאת, יצרנו סקריפט שלאחר הרצתו, משתני הסביבה יותאמו לעברית בצורה המיטבית. השורה האחרונה, תציג פלט על המסך שיאמר לנו מה ערכו של LC_ALL , הוא אמור להיות כמובן, בזכות הסקריפט: he_IL אני לא אכנס להסבר במדריך של הפקודות הנ”ל, שכן זה מעבר ל”תחום הלימוד” של הספר (ובכוונה נבחרו פקודות פשוטות).
עדיין אין שום דבר מסעיר ומיוחד, אבל כבר שימושי, ויכול להיות עוד יותר שימושי כחלק מסקרפיט גדול יותר.
אם אתה לא יודע מה הם משתנים, ככל הנראה לא למדת שפת תכנות מעולם.
משתנים הם למעשה מילים / רצף של אותיות לועזיות (כל מילה, גם חסרת משמעות שאינה מוגדרת בשפה למטרה אחרת, יכולה להתאים) יכולים לשמש כמשתנים.
משתנה מחזיק בתוכו ערך אחר. המשתנים ב bash, כמו בשפות shell אחרות, מתחילים תמיד ב $ ואח”כ בא המשתנה. זה ישמש אותנו במהשך לביצוע אוטומיזציה של דברים רבים. חשוב לבחור שמות משתנים (חוקיים, נדון בזה בהמשך) וכאלה שיהיה נוח להבין אותם (חשוב ביותר בסקריפטים ארוכים).אם למשל הסקריפט שלנו עוסק בחיבור לאינטרנט, סביר להניח שנרצה להשתמש בערך של ה ip לחיבור לרשת. הבחירה ב:
$Inet_Addres
נראית לי טובה - שימוש באות גדולה בתחילת כל מילה, וכן משתנה ברור, שברור משמו על מה הוא מצביע, זה אבן היסוד בבניית סקריפט נוח לשימוש. גם השמות הבאים יכולים להיות טובים :
$INET_ADDR $inet_addres $InetAdders
בחירת שמות המשתנים היא עניין של נוחות אישית.
שימו לב: אסור לכלול רווחים בשמות משתנים, אסור שהם יתחילו בספרות, אסור להשתמש בכל דבר אחר שהוא לא אותיות לועזיות או ספרות. ניתן כמה דוגמאות לשמות לא תקינים:
$Ine t $2intet אינטרנט$
הסימן דולאר אינו מוכר למי שאינו עוסק בתכנות בלינוקס / או בשפות הנגזרות משפות יוניקסיות כמו php, אבל אחרי שמתרגלים לשימוש בדולר, מבינים כי מדובר ברעיון די גאוני.
הנה דוגמא של תוכנית שתשתמש במשתנים: את התוכנית הבאה, אפשר להרית כתוכנית גיבוי של התוכן של ספריה כלשהו במחשב. אם למשל ברצוננו לגבות את התוכן של מה שנמצא בספרית הבית, ורוצים לעשות את זה בצורה החכמה הבאה. אציין שאפשר את הסקריפט הזה להריץ כ cron כך שיעבוד יום יום או כל פרק זמן קבוע, למשל פעם בשבוע ביום מסויים וישמור גיבוי יומי אוטומטי.
#!/bin/bash Save_As=/var/backup/$USER-$(date +%d%m%Y).tgz # The line above: into Save_As i put the path + and file name of the target echo "Save_As=$Save_As" # print on scrin the Save_As var tar -zcvf $Save_As ~ # make tar.gz of the directory ~ (which is Home dir)
הדוגמא הקודמת כבר מסובכת יותר ממה שהכרנו, ולכן נשתדל להבהיר אותה, על ידי הסבר מפורט שורה אחר שורה:
Save_As=/var/backup/$USER-$(date +%d%m%Y).tgz
בשורה הנ”ל יצרנו משתנה בשם Save_As שהוא למעשה שם הקובץ שאליו נשמור את הקובץ tgz שנייצר בעזרת tar. הקובץ יהיה כפי שברור תחת /var/backup. אבל מה שם הקובץ המדויק, כאן “התחבולה האמיתית” !
היות וסקריפט טוב הוא סקריפט שעובד בין משתמשים שונים, החוכמה היא לעשות אותו בצורה גמישה, ובהתאמה לכל משתמש. לפיכך, והיות ורציתי ששם הקובץ יכלול את מי הבעל בית שלו, כדי שיהיה ברור מי שמר אותו, הכנסתי בשם הקובץ את המשתנה $USER שהוא משתנה סביבה שמכיל את השם של המשתמש במערכת שמריץ את הסקריפט. הפרדתי אותו בעזרת התו ”-” (כדי שיהיה קריא בצורה נוחה) מהתאריך של יצירת הקובץ tgz.
איך אני מכליל בשם הקובץ את התאריך ? משתמש בפונקציה date שהיא פנימית ב-bash
למעשה, אפשר להריץ את הפקודה date ישירות מהשורת פקודה של bash ולראות את התאריך, בחרתי בפורמט של : יום (%d) ואח”כ חודש (%m) ואח”כ שנה (%Y). אפשר להריץ ישירות מהמעטפת את הפקודה:
date
לצורך העניין נניח ושם המשתמש הוא barak והקובץ נבנה ב 22/7/02 המשתנה Save_As אמור להיות שווה ערך ל:
/var/backup/barak-22072002.tgz
אם היינו משתמשים ב %y (במקום %Y) אז בשם הקובץ השנה היתה עם שתי ספרות, במקום 2002 אז היה מופיע 02
בשורה הבאה בסקריפט, השתשמשתי ב echo כדי לבדוק שאכן זה הפלט. השורה האחרונה
tar -zcvf $Save_As ~
היא שורה פשוטה של הרצת הפקודה tar כשאנחנו מעבירים לה את ה”דגלים” zcvf שאומרים לה ליצור קובץ tar.gz ששמו יהיה כפרמטר הראשון שמועבר (שהו $Save_As שערכו הוא כשם הקובץ שאנחנו רוצים ליצור) והפרמט השני הוא הספריה / קובץ שייצור את אותו קובץ ארכיב, במקרה שלנו זה ~ שהיא במערכות יוניקס, משמשתש כמעין משתנה סביבה שמסמל את ספרית הבית של מי שמריץ את הסקריפט (במקרה, שלי /home/barak )
כמובן שהסקריפט הזה, אילו הועבר לידי כל אחד מהקוראים, היה עובד נפלא גם אצלו. זה כל החכמה בסקריפטים - בניה נכונה שלהם, והם גמישים ביותר, ואינם תלויים בסביבה / מחשב / שם משתמש זה או אחר. הדוגמא הקודמת, על אף שהיא קצרה, הראתה את העצמה שיש ב bash ובשפת הסקריפט שלו.
עדיין לא נגענו בנושא “פונקציות” אבל היות והפרק עוסק במשתנים, יש חשיבות לדעת (בטרם נלמד את הנושא לעומק) שיש בכמעט כל שפה, וגם ב bash מונח הקרוי פונקציה. פונקציה היא חלק של תוכנית שניתן לקרוא אליה כל אימת שרוצים, והיא כוללת סדרה של הוראות. אם למשל יודעים שרוצים לעשות פעולה מסויימת, שדורשת x שורות כמה פעמים בתוכנית, עדיף להשתמש בפונקציה (שנכתבת רק פעם אחת) ופשוט לקרוא לה כל אימת שרוצים.
אחד הייסודות בתכנות נכון, הוא להשתמש בכמה שפחות משתנים כללים, שהם משתנים שערכם נקבע לאורך כל התוכנית. או במילים אחרות: אפשר לקבוע לפונקציות, משתנים מקומיים, שיוכרו רק בתוך הפונקציות. זו אחת הדרכים להמנע משגיאות תיכנותיות, ולכתיבת תוכנית ברורה ונוחה לשימוש.
הנושא הזה פחות חשוב בשפות סקריפט קלות כמו bash אבל עדיין, אנשי ה bash נתנו לנו את הכלי לזה, וכשנדרשים לו, טוב לדעת שהוא קיים.
הדוגמא הבאה תלמד אותנו להמנע מטעות די נפוצה בעבודה עם bash:
#!/bin/bash Number=2 Number2=3 echo Now we do some mistake ! echo echo Number+Number2 # on screen we will see 2+3 echo Now we get 5 on screen: echo echo $[$Number+$Number2] echo Another option: echo $(($Number1 + $Number2))
רצוי שתריץ את הסקריפט ותשחק עמו מעט. בסה”כ, הבעיה די ברורה, וחבל לטעות, כשאין סיבה.
ראשית נגדיר מספר מושגים:
המושגים הנ”ל מתייחסים למערכת הקלט /פלט הסטנדרטית.
בפועל, stdout קובע לאן יישלך הפלט (בד”כ למסך) stdin הוא קליטת קלט (בד”כ מקלדת) ו stderr הוא פלט שנוצר משגיאות תוכנית, ואפשר לנתב אותו כמו stdout למסך, או למקום אחר (כמו קובץ)
למה הסברתי את זה זה? כי redirection מאפשר לנתב פלט של תוכנית לתוכנית שמסוגלת לקלוט אותו, או לשלוח פלט לתוך קובץ וכו'. אם לא הבנת על מה דיברתי, לא נורא, בסעיף הבא נראה דוגמא מעשית (המתכנתים שביננו בטח מחייכים מאושר, כי הנושא הזה סביר להניח בהיר להם לחלוטין).
סביר להניח, שגם אם אתה עוד לא הבנתם במה מדובר, השתמשתם בזה כבר בשימוש יומיומי במחשב בלי לשים. ניקח את הדוגמא הבאה:
ls /home/barak/mp3 > mp3.txt
כאן השתמשנו בפקודה ls שאמוה להציג על המסך את תוכן התקיה, במקרה הזה ספרית ה mp3 הצנועה שלי, אבל במקום שהוא תוצג על המסך, השתמשתי ב ”<” כדי לאמר ל מפרש הפקודות, שאני רוצה לנתב את הפלט לקובץ בשם : mp3.txt ועכשיו יש לי את רשימת השירים הצנועה בתור קובץ טקסט פשוט. עוד דוגמא:
tar xvf > file.txt
היות ולא ניתן ל tar שם קובץ, הוא ישלח הודעת שגיאה בעזאת stderr, במצב רגיל זה נשלח למסך. במקרה שלנו, השגיאה הועברה לתוך קובץ txt.
סיכום: הסעיף הזה דן בנושא מאוד כללי, שרובנו מכירים אותו לא מתוך שימוש בסקריפטים, אבל היות והוא חשוב (וכרגיל, כל מה ש bash עושה מחוץ לסקריפט ישירות בשורת פקודה, היא יודעת לעשות גם בתור שפת סקריפט), דנו בזה בצורה שטחית משהו, אבל ממצה את מה שחשוב לשימוש היומיומי
אני מניח שכבר יש לך ניסיון עם זה (שכן מדובר באפשרות מאוד שימושית במערכות לינוקס). ובכל מקרה, בשל החשיבות הסבר קצר: האפשרות של שרשור פלט, היא היכולת של תוכניות מעטפת להעביר פלט של תוכנית אחת לקלט של התוכנית השניה. זה נעשה באמצעות הסימן | שנמצא בד”כ במקלדת מימין ל shift הימני. יש צורך ללחוץ shift בצירוף של אותו מקש. דוגמא לשימוש בפקודה הזאת, ישירות מה shell:
rpm -qa | grep kde
מה שעשינו זה (במערכות מבוססות rpm) קיבלנו בעזרת rpm -qa את שמות כל החבילות המותקנות במחשב. במקום להציג אותן על המסך, שירשרנו את הפלט לתוך הפקודה grep הפקודה grep תוציא כפלט רק את מה שקשור ל kde. כמובן שיכלנו לבצע עוד שירשורים, ככל שרק יידרש.
האפשרות לשרשר נתונים היא אבן בסיס בשימוש בלינוקס, ולכן חובה להכיר את זה היטב.
משפטי תנאי כשמם כן הם: הם משמשים להרצת חלק בתוכנית / פקודות רק “בתנאי ש”.
השימושיות של משפטי התנאי והלולאות (שעליהם נדבר בהמשך) היא גבוהה מאוד, ולמעשה, מרבית העצמה של רוב שפות התכנות באות משימוש בהם.
למשפטי תנאי יש מספר תבניות. מדובר בלולאות מסוג “if” כשיש מס' תבניות אפשרות:
if [condition]; then ##execute this code - between if and fi fi
הנה דוגמאת קוד אמיתית לשימוש ב if:
#!/bin/bash Ip_Enable=`cat /proc/sys/net/ipv4/ip_forward` if [ $Ip_Enable=0 ]; then echo IP_Forword isn't enabe fi
כמובן שיכלנו לעשות משהו יותר שימושי. נסביר את הקוד הקודם צעד אחר צעד: השורה השניה בסקריפט, משתמשת ב תו (`) שהוא התו שנמצא במקלדת בד”כ משמאל לספרה 1 (מעל למקש tab) היא זמינה כאשר המקלדת במצב אנגלית. ב bash משתמשים בה כדי להריץ פקודה, כמו בדוגמא. הפקודה רצה והפלט שלה נכנס למשתנה, במקרה שלנו Ip_Enable.
בדוגמא שלנו אנחנו מוציאים כפלט את הערך של הקובץ /proc/sys/net/ipv4/ip_forward - קובץ זו הוא קובץ שבו ישנו הערך 0 כאשר ip_forwording אינו מאושפר או 1 כאשר הוא מאופשר (חשוב לאפשר אותו כאשר רוצים לשתף מחשבים באינטרנט דרך מכונת הלינוקס ).
לולאת ה if בדוגמא די ברורה: אם הערך של הקובץ הוא 0 אז על המסך תופיע הודעה: IP_Forword isn't enabe
if [ condition]; then #execute code else #execute the code here fi
אם נרצה לתת דוגמא של הנושא, נרחיב את הדוגמא הקודמת:
#!/bin/bash Ip_Enable=`cat /proc/sys/net/ipv4/ip_forward` if [ $Ip_Enable=0 ]; then echo IP_Forword isn't enabe else echo IP_Forword is enable fi
היות ואני מניח שהתוכנית ברורה, נמשיך הלאה, בלי להסביר.
הצורה הכללית של זה היא הבאה:
if[conditionA];then ##do some code elif [conditionB];then ##Do something else ##Do somting fi
נציין רק שאפשר להשתמש בכמה רמות של else שרוצים לאותו משפט if. אין טעם לתת דוגמא ספציפית של שימוש ב elif כי שימוש מעשי בתבנית זו יעשה בפרקים הבאים.
כמו בכל שפת תכנות גם בסקריפטים של bash נוהגים להשתמש בלולאות. לולאות מאפשרות לבצע קטע קוד x פעמים, בהתאם לצרכים של התוכנית. הלולאה מתקיימת כל עוד מתקיים תנאי שבודק את הלולה כפי שיובהר בהמשך. הלולאות הקיימות ב bash מתחלקות ל 3 סוגים עיקריים:
התבנית של לולאת while הוא הבא:
while [condition]; do some code here done
קטע הקוד בין while ל done מתבצע על עוד מתקיים התנאי של while.
דוגמא לשימוש בלולאת while: הדוגמא היא דוגמא אמיתית, שיכולה להיות שימושית:
נניח ויש לנו סקריפט לטיפול בחיבור לאינטרנט מסוג isdn, ואנחנו רוצים אחרי החיבר, לבצע פעולה כלשהי, אבל שהיא תתבצע רק אחרי שיתקבל ip מהספק אינטרנט, אפשר להשתמש בדוגמא הבאה:
#!/bin/bash INET_IP=`/sbin/ifconfig ippp0 | grep 'inet addr' | awk '{print $2}'| sed -e 's/.*://'` #Get IP while [ ! $INET_IP ]; do #While we dont have IP echo wait to get IP #msg on screen sleep 1 # wait 1 second #wait 1 second INET_IP=`/sbin/ifconfig ippp0 | grep 'inet addr' | awk '{print $2}'| sed -e 's/.*://'` #Get Ip done
עכשיון נסביר שורה שורה: השורה:
INET_IP=`/sbin/ifconfig ippp0 | grep 'inet addr' | awk '{print $2}'| sed -e 's/.*://'` #Get IP
היא מסובת מעט למראה, אבל פשוט יחסית, אם מכירים את הכלים שבשימוש בה: ראשית מפעילים את /sbib/ifconfig ippp0 פקודה זו מוציא כפלט את הנתונים שקשורים לחיבור של ippp0 (חיבור מסוג isdn אפשר לעשות את אותו טריק על ppp0 למשל, לבעלי חיבור מסוג זה) . דוגמא לפלט:
ippp0 Link encap:Point-to-Point Protocol inet addr:62.0.112.190 P-t-P:212.143.200.243 Mask:255.0.0.0 UP POINTOPOINT RUNNING NOARP MTU:1500 Metric:1 RX packets:92 errors:0 dropped:0 overruns:0 frame:0 TX packets:95 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:30
את הפלט אנחנו מעבירים באמצעות pipeline לפקודת grep שתבחר את השורה הספציפית הרצויה לנו, או במילים אחרות זאת עם 'inet addr'.
inet addr:62.0.112.190 P-t-P:212.143.200.243 Mask:255.0.0.0
את הפלט של אותה שורה מעבירים ל awk שבוחר את הפרמטר השני ($2) כאשר החלוקה לפרמטרים היא לפי בלוקים עם רווח. אז מה שייבחר בדוגמא שלנו הוא:
addr:62.0.112.190
עדיין נדרש לבחור רק את ה ip מתוך השורה שנותרה, כדי לקבל אותה מה שנותר זה להשתמש ב sed תוך שימוש ב regexp. זה חורג ממסגרת המדריך, ולכן לא נסביר את התיאוריה של נושא זה (יש תיעוד מלא בכל מערכת לינוקס לנושא זה).
אם כן, INET_IP מקבל את הערך של ה ip של המחשב.
מה שנותר זה לבצע לולאת while, שבא כל עוד אין ip תקני (כלומר יש ערך null ל ip - זה קורא כשמתחברים, עד שהספק נותן ip או כל עוד יש שגיאה שכן אין ippp בכלל, כשמנותקים), יתקיים המשפט שבתוך לולאת ה while במקרה שלנו.
בתוך תנאי ה while השתמשתי באופרטור ! - למה? האופרטור הזה אומר שהביטוי שנבדק, מתבצע עליו מבחינת ה while היפוך לוגי. כלומר: אם הביטוי הוא שקר, אז מבחינת ה while הוא אמת, ולהיפך.
הודעה למסך, אח”כ המתנה של שניה אחת, ולאח”כ איתחול מחדש של המשתנה INET_IP כדי שהבדיקה תוכל להתבצע על INET_IP מחדש.
הסקריפט יכל להעשות בצורה מתוחכמת יותר, אבל ניסיתי לפשט אותו למטרת הלימוד.
אם השורה של איתחול המשתנה $INET_IP מעט מבלבלבת, זה הגיוני, וזה מבלבל רבים שעושים את צעדיהם בתחום. נסה להתעמק בפקודה שוב, ולחלק אותו לחלקים השונים, ולנתח חלק חלק.
כמו כן, תכנות SHELL מסתמך המון על כלים כמו SED AWK U ו GREP ולכן מומלץ מאוד ללמוד אותם (גם אם לא לעומק, לפחות לדעת מה הם עושים ואיך משתמשים בהם).
לולאה זו דומה מאוד ללולאה while בהבדל אחד: היא מתקיימת כל עוד התנאי הוא “שקר”. דוגמא:
#!/bin/bash INET_IP=`/sbin/ifconfig ippp0 | grep 'inet addr' | awk '{print $2}'| sed -e 's/.*://'` #Get IP until [ $INET_IP ]; do #until we will get IP echo wait to get IP #msg on screen sleep 1 # wait 1 second #wait 1 second INET_IP=`/sbin/ifconfig ippp0 | grep 'inet addr' | awk '{print $2}'| sed -e 's/.*://'` #Get Ip done
הפעולה הזאת זהה לפעולה בסעיף הקודם. אם כן until מאפשר לנו לממש את הסקריפט בצורה קריאה ונעימה יותר, אבל הוא אינו מוסיף “שום כוח” לפקודה “while”.
לולאת for היא לולאה מאוד מיוחדת, ושימושית מאוד בשפות תכנות רבות. זה המבנה של הלולאה:
for $var in 'seq num1 to num2' do #some code done
אם נראה להראות דוגמא “מהעולם האמיתי”:
#!/bin/bash NUMBER=0 for file in $( ls ); do let NUMBER+=1 echo file $NUMBER: $file done
מה שעשינו בדוגמא הנ”ל זה: הלולאת for מתבצעת כל עוד יש עוד קובץ שיוכנס לתוך המשתנה file (שם הקובץ מוכנס). בתוך הלולאה אנחנו מבצעים פשוט הצגה של שם הקובץ. עכשיו נסביר שורה שורה:
NUMBER=0
שורה זו איתחלה את הערך 0 לתוך המשתנה.
for file in $( ls ); do
שורה זו אומרת לבצע את מה שבתוכן הלולה כל עוד הפקודה ls מתקיימת, ואת הפלט שלה לשים בתוך משתנה בשם file
let NUMBER+=1
שורה זו שימשה אותנו להגדיר את המשתנה NUMBER ב 1. צורת הרישום +=1 שקולה ל:
let NUMBER=NUMBER+1
echo file $NUMBER: $file
הוצאת פלט של שמות הקבצים על המסך, בתצורה: file_number:file_name
דוגמא זו אינה שימושית מדי, אבל אמורה להביר את היכולות של לולאת for
הממשק של התוכנית, הוא חלק חשוב מאוד.
כשאתה בונה את הסקריפט לעצמך, אתה יכול להסתפק לעיתים בממשק “פרימיטיבי” ופשוט ביותר, שכן אתה מכיר את התוכנית ויודע איך להשתמש בה.
כשאתה בונה סקריפט שאמור לשמש לא רק אותך, או שאתה רוצה להיות בטוח שגם אם תזקק לו לאחר תקופה של חוסק שימוש, תוכל להשתמש בו בקלות, חשוב לתת קצת יותר מחשבה על ממשק נוח.
הודעות ידידותיות, שכוללות תפריט שמאפשר לקבל הסבר על אופציות,במידת הצורך, יכול להיות לעניין. אם למשל הסקריפט אמור להיות סקריפט של מערכת ל”קינפוג” פיירוואל, ואתה אומר למשתמש בתפריט:
Which type of network you using? 1. ippp0 2. ppp0 3. pptp0
זה יכול להיות לא הדבר הכי נוח בעולם (במידה והסקריפט נועד למשתמשים, חדשים, אבל למשל, תפריט הרבה יותר קריא למרבית המשתמשים יכול להיות:
Which type of network you using? 1. ippp0 - isdn 2. ppp0 - dialup 3. pptp0 - ADSL
ייתכן מאוד והדוגמא לעייל לא נכונה (אני לא יודע איזה סוג חיבור יש ב adsl ויש גם כבלים וכו', העיקר היה הרעיון שבדוגמא). לסיכום: כשאתה בונה סקריפט, תמיד תנסה לחשוב מהצד של הלקוח השמתמש בו, ולא מהצד שלך כבונה הסקריפט, אחרת הוא יהיה לא ידידותי, והמדריך לשימוש בסקריפט יהיה יותר ארוך, ומייגע מהסקריפט עצמו. עוד הערה חשובה על הממשק - חשוב שהממשק יהיה באנגלית, שכן לא בכל מחשב מוסדרות שפות אחרות (שלא לדבר על העברית שהיא בעייתית ביותר בכל מה שנוגע לסביבות המעטפת )
בפרק זה, נבהיר בהמשך, איך בונים ממשק (מהיבט טכני), ולא נתעמק בשיקולים של בניית ממשק חכם. אתה יכול להתייחס לנאמר בסעיף זה כ”טיפים” לבנית ממשק, וכמובן שההמלצות לא מחייבות.
תחביר הפקודה:
OPTIONS="option1 option2 optionN" select option in $OPTIONS; do if ["$option" = "option1"] #do somthing elif ["$option" = "option2"] #do somthing elif ["$option" = "optionN"] done
הרעיון מאחורי select הוא ש-bash נותן על המסך את האפשרויות השונות שהמשתמש יכול לבחור. במילים אחרות, על המסך יופיעו שורה אחר שורה המחרוזות של option1 option2 וכו'… כשלפניהם יש ספרה מייצגת (הראשון כ 1, השני כ 2 וכו') המשתמש יצטרך לבחור אחד מהם , על ידי הקלדת המס' הרצוי. המשתנה option הוא זה שיקבל את הערך הנלחץ. לפיכך, אפשר לבנות תפריטים בצורה פשוט.
התפריט נשאר על המסך, כל עוד אין exit מהלולאה… כלומר על המתכנת לדאוג כשהתפריט כבר לא הופל לרלוונטי לתת פקודת exit לא ממשה הבנת ? לא קרה כלום, הדוגמא הבאה תהיה ברורה אני חושב:
#!/bin/bash echo Choose 1 of the next options OPTIONS="still_in exit exit_with_bye" select opt in $OPTIONS; do if [ "$opt" = "exit" ]; then exit elif [ "$opt" = "exit_with_bye" ]; then echo bye exit elif [ "$opt" = "still_in" ]; then echo stiil in else clear echo bad option fi done
הרץ את הדוגמא הנ”ל, ונסה להבין את הקוד לבד, הוא די ברור.
ובכל זאת הסבר קצר:
OPTIONS="still_in exit exit_with_bye" select opt in $OPTIONS; do
יגרום להופעה על המסך של תפריט פשוט עם האפשרויות: still_in , exit ושל exit_with_bye
בפועל זה נראה בערך ככה:
[barak@beitar bash]$ ./select Choose 1 of the next options 1) still_in 2) exit 3) exit_with_bye #?
המשתמש צריך לבחור בין האפשרויות 1 או 2 או 3. אם המשתמש בוחר באפשרות 1, יתקיים התנאי
elif [ "$opt" = "still_in" ]; then
מה שאומר שמה שיורץ בלולאה זה:
echo stiil in
שיתן פלט שאנחנו עדיין בתוכנית, המשתמש יוכל לבחור מחדש משהו אחר. אם הוא יבחר באפשרות 2 יתקיים התנאי הבא:
if [ "$opt" = "exit" ]; then
ולכן מה שיורץ על ידי הסקריפט יהיה exit יציאה מהסקריפט בלי שום משוב.
אני לא אסביר את האפשרויות הנוספות הן די ברורות, אני מניח. בסה”כ מדובר באפשרות ליצירת תפריט מאוד פשוט, מעט מוגבל, מבחינת אפשרויות העיצוב, אבל נוח מאוד ליצירה, שכן ה bash מעצב את התפריט ועושה את כל העבודה לבד בפועל.
כשהמשתמש מפעיל תוכנית כלשהי, הוא בהרבה מקרים, רוצה להעביר פרמטר כלשהו אם ניקח דוגמא בסיסית:
ls -l
המשתמש העביר את הדגל l לפקודה. כמובן שסקריפטי bash תומכים בהעברת פרמטרים. המימוש הוא די פשוט. עכשיו לדוגמא של סקריפט bash שמממש קבלת פרמטרים:
#!/bin/bash if [ -z "$1" ]; then echo you need to pass your name as parameter to: $0 exit fi echo hello $1 $2
במקרה הנ”ל אם לא הועבר לפחות פרמטר אחד (הראשון) הסקריפט תצא החוצה עם ההודעה:
you need to pass your name as parameter to: filename
אם העברנו לפחות פרמטר אחת תופעל השורה:
echo hello $1 $2
מה שאומר שיוצג על המסך:
hello parameter1 parameter2
אם למשל שם הקובץ הוא: name אז ניקח כמה דוגמאות של הרצה והפלט:
[barak@beitar bash]$ ./name you need to pass your name as parameter to: ./name
[barak@beitar bash]$ ./name barak hello barak
[barak@beitar bash]$ ./name barak bloch hello barak bloch
ולסיום נעביר גם פרטמטרים נוספים:
[barak@beitar bash]$ ./name barak bloch is Beitar Jrusalem fan hello barak bloch
כמובן שהפרמטרים אחרי הפרמטר השלישי לא יוצגו, שכן לא הצגנו את 3$ וכו'….
אם ננסה להסביר בכמה מילים את מה צורת העבודה של העברת הפרמטרים מה”שורת פקודה” אפשר לאמר שמה שקורה זה בסה”כ שבתוכנית אפשר בכל שלב לפנות למשתנים מיוחדים בשם שמיוצגים על ידי מספרים (ולא אותיות) שהם למעשה הפרמטרים שמועברים. לכל פרמטר שמועבד יש משתנה משלו, שהוא המס' של הפרמטר. כלומר:
אני בטוח שאנשי c/c++ מחייכים בהנאה מהפשטות שבא עובד ה bash גם בנושא הזה. הכל מאוד פשוט.
בעזרת read ניתו לקבל קלט מהמשתמש. ברגע שהשתמשתנו ב read התוכנית מקווה שהמשתמש יקליד משהו, ואת הנתונים שהוא מקליד מכניסים לתוך משתנים. התבנית היא כדלקלמן:
read var1 var2... varn
אח”כ אפשר להתמש ב var1 var2 וכו'… אנחנו ניתן כאן דוגמא פשוטה:
#!/bin/bash echo Please, enter something read SOMETHING echo "echo $SOMETHING"
פונקציות הם קטעי קוד שמאוגדים תחת מזהה מהסויים (שם הפונקציה) ואשר ניתן לקרוא להם בעת הצורך. היתרון בשימוש בפונקציות הוא חלוקת הסקריפט למטלות קטנות, כששימוש נבון בפונקציות, אמור ליצור פונקציות שאינן תלויות אחת בשניה. בצורה זו אפשר להעביר פונקציות בשלמותן מסקריפט אחד למישנהו בעת הצורך.
אם למשל כתבנו פונקציה שהמטלה שלה היא איתחול של הפיירוואל, והיא חלק מחיבור לאינטרנט, אפשר יהיה לנייד את אותה פונקציה, בהנחה והיא תכתב נכון (ובאופן בלתי תלוי בתוכנית, שזה דבר שיוסבר בהמשך) לסקריפט אחר במידה ונרצה להחליף סקריפט חיבור לרשת.
הדוגמא הזאת היא לא בהכרח דוגמא שימושית (בד”כ איתחול הפיירוואל נעשה בסקריפט יחודי ולא דרך סקריפט החיבור לרשת ישירות, אבל זה לצורך ההדגמה).
כרגיל בשפות מעטפת, גם השימוש בפונקציות הוא פשוט מאוד. איך זה נעשה?
#!/bin/bash function quit { echo now exit exit } function hello { echo Hello sleep 1 } hello quit
אז מה התוכנית הנ”ל עושה ? נבחן את הקטעים השונים:
function quit { echo now exit exit }
בקטע הנ”ל הגדרנו פונקציה, בעזרת השימוש במילה function ששמה הוא quit שכל מה שהיא עושה זה מציגה את הפלט “now exit” ולאחר מכן יוצאת מהתוכנית. בעת הגדרת הפונקציה, היא לא מופעלת אלא רק לאחר שהמשתמש קורא לה, וזה יקרה בהמשך.
function hello { echo Hello sleep 1
מגדירים עוד פונקציה ששמה הוא hello שהיא מציגה על המסך hello וממתינה שניה בטרם התוכנית תמשיך לרוץ (sleep 1)
עד כה התוכנית פעלה, אך לא עשתה שום דבר בפועל, וכמובן לא הצגיה כלום. ואז מגיעים לקטע של הקריאה לפונקציות:
hello quit
מה שקורה זה שכאן הפונקציות “נקראות” לכן הם ירוצו. קריא ל hello תגרום להצגה על המסך של hello ואח”כ להשהייה של שניה. בתום שניה זאת, תופעה הפונקציה quit. והתוכנית תצא לשורת הפקודה. כמו בשפות תכנות אחרות, ניתן להעביר פרמטרים לפונקציה למשל:
#!/bin/bash function msg { echo $1 } msg Hello
התוכנית הפשוטה הזאת מה שהיא עושה זה מעבירה את הפרטמר Hello לתוך הפונקציה שנקראת msg. ואז היא מבצעת הצגה של הפרמטר שמועבר לפונקציה ($1 הפרמטר הראשון שמועבר, ממש כמו בהעברת פרמטרים לתוכנית מה shell).
עכשיו לדוגמא מעט מסובכת יותר של שימוש ב read (נלמד בפרק הקודם) ובהעברת פרמטרים לפונקציה. כך את הדוגמא הבאה, כאפשרות לבנות תפריט כחלק מסקריפט של התקנה של תוכנה, כאשר הסקריפט נזקק לדעת מהי ההפצה של המתמש, ולכן הוא נדרש לבחור אותה מהתפריט. אחרי הבחירה, התוכנית תוכל להתקין את התוכנה בהתאם להפצה. בתוכנית שלנו, רק נציג הודעה שקשורה ל”הפצה” הנבחרת. במקרה אמיתי, אפשר להוסיף שם גם חלק ממשי. התוכנית הולכת ככה:
#!/bin/bash echo Please, choose your Linux dist. echo 1.Red Hat echo 2.Mandrake echo 3.Suse echo 4.Slackware echo 5.LFS read dist function install { if [ $1 = 1 ]; then echo install on Red Hat elif [ $1 = 2 ]; then echo Install on Mandrake elif [ $1 = 3 ]; then echo Install on Suse elif [ $1 = 4 ]; then echo Install on Slackware elif [ $1 = 5 ]; then echo Install on LFS else echo wrong choise, try again read dist install $dist fi } install $dist
נסביר בקצרה את התוכנית (היא כשלעצמה די ברורה):
כמו כן הוספני בתחום ה elif את else למקרה שבו הוקלדה בתפריט אפשרות שלא קיימת, התוכנית תציג מחדש את התפריט, כשהיא שוב קוראת קלט מהמשתמש.
לא תוכנית שימושית מדיי, אבל תמיד יכול להיות נחמד, בתור תוכנית ללימוד של עקרונות שונים:
#!/bin/bash if [ -z "$1" ]; then echo you need to pass your name as parameter to: $0 exit fi for i in `seq 1 $1`;do ##loop until i=$1 ($1=user parameter) sleep 1 # sleep 1 second clear #clear screen echo $i #echo number (1 - > $i) done