תכנות עם Bash

ברק בלוך
היסטוריית גירסאות
גירסה 0.2 08-04-2007 כתריאל טראום
הומר לפורמאט ויקי
גירסה 0.2 22-07-2002 ברק בלוך
גירסא ראשונה

זכויות יוצרים ורשיון

כל הזכויות שמורות © 2007, ברק בלוך, הרשות ניתנת להעתיק, לשנות ולהפיץ מדריך זה תחת התנאים של רשיון ה-GFDL
Linux הוא סימן מסחרי רשום של Linus Torvalds.

הסרת אחריות

הכותב אינו נושא באחריות עבור שימוש ברעיונות, דוגמאות ומידע שבמדריך. השימוש הוא באחריות הקורא בלבד. המדריך עשוי להחיל טעויות ופרטים לא נכונים, שהשימוש בהם עשוי להיות מזיק למחשבך. למרות הסבירות הנמוכה, הכותב אינו לוקח כל אחריות

מבוא

מה זה bash ?

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

כמו בכל מדריך ללימוד שפת תכנות / סקריפט - הסקריפט הראשון שלנו לא יעשה דבר פרט מפלט סטנדרטי של “hello world”.

ראשית אסביר בקצרה מהם השלבים לבניית סקריפט:

  1. פתיחת עורך הטקסט החביב עליך (אני עבד עם vim).
  2. כתיבת הקובץ בעורך, ושמירתו בשם סביר (למשל תוכנית hello world אפשר לשמור כ - hello ).
  3. הערה:שם הקובץ לא חשוב מבחינת השפה, רק לנוחות שלך
  4. מתן הרשאת ריצה לקובץ - בד”כ chmod 755 script_name יעשה את העבודה.
  5. הרצת הקובץ על ידי:
./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))

רצוי שתריץ את הסקריפט ותשחק עמו מעט. בסה”כ, הבעיה די ברורה, וחבל לטעות, כשאין סיבה.

הכוונת נתונים ו pipeline

מה זה הכוונת נתונים / redirection

ראשית נגדיר מספר מושגים:

  1. stdout = Standard output
  2. stdin = Standard Input
  3. stderr = Standard Error

המושגים הנ”ל מתייחסים למערכת הקלט /פלט הסטנדרטית.
בפועל, stdout קובע לאן יישלך הפלט (בד”כ למסך) stdin הוא קליטת קלט (בד”כ מקלדת) ו stderr הוא פלט שנוצר משגיאות תוכנית, ואפשר לנתב אותו כמו stdout למסך, או למקום אחר (כמו קובץ)

למה הסברתי את זה זה? כי redirection מאפשר לנתב פלט של תוכנית לתוכנית שמסוגלת לקלוט אותו, או לשלוח פלט לתוך קובץ וכו'. אם לא הבנת על מה דיברתי, לא נורא, בסעיף הבא נראה דוגמא מעשית (המתכנתים שביננו בטח מחייכים מאושר, כי הנושא הזה סביר להניח בהיר להם לחלוטין).

אז איך משתמשים בהכוונת נתונים?

סביר להניח, שגם אם אתה עוד לא הבנתם במה מדובר, השתמשתם בזה כבר בשימוש יומיומי במחשב בלי לשים. ניקח את הדוגמא הבאה:

ls /home/barak/mp3 > mp3.txt

כאן השתמשנו בפקודה ls שאמוה להציג על המסך את תוכן התקיה, במקרה הזה ספרית ה mp3 הצנועה שלי, אבל במקום שהוא תוצג על המסך, השתמשתי ב ”<” כדי לאמר ל מפרש הפקודות, שאני רוצה לנתב את הפלט לקובץ בשם : mp3.txt ועכשיו יש לי את רשימת השירים הצנועה בתור קובץ טקסט פשוט. עוד דוגמא:

tar xvf > file.txt

היות ולא ניתן ל tar שם קובץ, הוא ישלח הודעת שגיאה בעזאת stderr, במצב רגיל זה נשלח למסך. במקרה שלנו, השגיאה הועברה לתוך קובץ txt.

סיכום: הסעיף הזה דן בנושא מאוד כללי, שרובנו מכירים אותו לא מתוך שימוש בסקריפטים, אבל היות והוא חשוב (וכרגיל, כל מה ש bash עושה מחוץ לסקריפט ישירות בשורת פקודה, היא יודעת לעשות גם בתור שפת סקריפט), דנו בזה בצורה שטחית משהו, אבל ממצה את מה שחשוב לשימוש היומיומי

מה זה pipeline - (שרשור פלט)

אני מניח שכבר יש לך ניסיון עם זה (שכן מדובר באפשרות מאוד שימושית במערכות לינוקס). ובכל מקרה, בשל החשיבות הסבר קצר: האפשרות של שרשור פלט, היא היכולת של תוכניות מעטפת להעביר פלט של תוכנית אחת לקלט של התוכנית השניה. זה נעשה באמצעות הסימן | שנמצא בד”כ במקלדת מימין ל 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 elif else

הצורה הכללית של זה היא הבאה:

if[conditionA];then
##do some code

elif [conditionB];then
##Do something
else
##Do somting
fi

נציין רק שאפשר להשתמש בכמה רמות של else שרוצים לאותו משפט if. אין טעם לתת דוגמא ספציפית של שימוש ב elif כי שימוש מעשי בתבנית זו יעשה בפרקים הבאים.

השימוש בלולאות

מהן לולאות

כמו בכל שפת תכנות גם בסקריפטים של bash נוהגים להשתמש בלולאות. לולאות מאפשרות לבצע קטע קוד x פעמים, בהתאם לצרכים של התוכנית. הלולאה מתקיימת כל עוד מתקיים תנאי שבודק את הלולה כפי שיובהר בהמשך. הלולאות הקיימות ב bash מתחלקות ל 3 סוגים עיקריים:

  • לולאת while
  • לולאת until
  • לולאת for

לולאת while

התבנית של לולאת 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 ולכן מומלץ מאוד ללמוד אותם (גם אם לא לעומק, לפחות לדעת מה הם עושים ואיך משתמשים בהם).

לולאת until

לולאה זו דומה מאוד ללולאה 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 היא לולאה מאוד מיוחדת, ושימושית מאוד בשפות תכנות רבות. זה המבנה של הלולאה:

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 ויש גם כבלים וכו', העיקר היה הרעיון שבדוגמא). לסיכום: כשאתה בונה סקריפט, תמיד תנסה לחשוב מהצד של הלקוח השמתמש בו, ולא מהצד שלך כבונה הסקריפט, אחרת הוא יהיה לא ידידותי, והמדריך לשימוש בסקריפט יהיה יותר ארוך, ומייגע מהסקריפט עצמו. עוד הערה חשובה על הממשק - חשוב שהממשק יהיה באנגלית, שכן לא בכל מחשב מוסדרות שפות אחרות (שלא לדבר על העברית שהיא בעייתית ביותר בכל מה שנוגע לסביבות המעטפת )

בפרק זה, נבהיר בהמשך, איך בונים ממשק (מהיבט טכני), ולא נתעמק בשיקולים של בניית ממשק חכם. אתה יכול להתייחס לנאמר בסעיף זה כ”טיפים” לבנית ממשק, וכמובן שההמלצות לא מחייבות.

הפקודה select

תחביר הפקודה:

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”:
[barak@beitar bash]$ ./name barak
hello barak
  • במקרה השלישי נעביר את הפרמטרים: barak bloch
[barak@beitar bash]$ ./name barak bloch
hello barak bloch

ולסיום נעביר גם פרטמטרים נוספים:

[barak@beitar bash]$ ./name barak bloch is Beitar Jrusalem fan
hello barak bloch

כמובן שהפרמטרים אחרי הפרמטר השלישי לא יוצגו, שכן לא הצגנו את 3$ וכו'….

אם ננסה להסביר בכמה מילים את מה צורת העבודה של העברת הפרמטרים מה”שורת פקודה” אפשר לאמר שמה שקורה זה בסה”כ שבתוכנית אפשר בכל שלב לפנות למשתנים מיוחדים בשם שמיוצגים על ידי מספרים (ולא אותיות) שהם למעשה הפרמטרים שמועברים. לכל פרמטר שמועבד יש משתנה משלו, שהוא המס' של הפרמטר. כלומר:

  • משתנה ראשון שמועבר, הוא $1
  • המשתנה השני הוא 2$
  • משתנה n הוא כמובן n$

אני בטוח שאנשי c/c++ מחייכים בהנאה מהפשטות שבא עובד ה bash גם בנושא הזה. הכל מאוד פשוט.

שימוש ב read לקבלת קלט מהמשתמש

בעזרת 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

נסביר בקצרה את התוכנית (היא כשלעצמה די ברורה):

  • אנחנו מציגים על המסך אפשרויות שונות לבחירה.
  • אח”כ בעזרת read מקבלים את המשתנה $dist שהוא למעשה הספרה המצייגת את ההפצה הנבחרת.
  • אח”כ באה הגדרת הפונקציה, שכמובן בשלב זה לא רצה.
  • ואז מריצים אותה בשורה install $dist
  • הפונקציה רצה כשמועבר אליה הפרמטר $dist
  • בפונקציה יש שרשרת של לולאות if elif שבודקות מול המשתנה שהועבר איזה הפצה היא בשימוש, ומציגים הודעה רלוונטית.

כמו כן הוספני בתחום ה 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
מדריכים/תכנות_עם_bash.txt · שונה לאחרונה ב: 2008/06/19 18:34 (עריכה חיצונית)
chimeric.de = chi`s home Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0