היסטוריית גירסאות | ||
---|---|---|
גירסה 0.9 | 07-01-2005 | <כרמית לוי> |
טיוטא ראשונה |
מדריך זה נוצר על מנת לעזור להתחיל לתכנת ברמה בינונית בבש. מדריך זה אינו תכנות של רמה מתקדמת ומכיל דוגמאות קוד. מדריך זה מתורגם ממדריך באתר הדוקמנטציה tldp.
כל הזכויות שמורות © 2007, <כרמית לוי>, הרשות ניתנת להעתיק, לשנות ולהפיץ מדריך זה תחת התנאים של רשיון ה-GFDL
Linux הוא סימן מסחרי רשום של Linus Torvalds.
הכותב אינו נושא באחריות עבור שימוש ברעיונות, דוגמאות ומידע שבמדריך. השימוש הוא באחריות הקורא בלבד. המדריך עשוי להחיל טעויות ופרטים לא נכונים, שהשימוש בהם עשוי להיות מזיק למחשבך. למרות הסבירות הנמוכה, הכותב אינו לוקח כל אחריות
הכותב מודה לאנשים הבאים על תרומתם למדריך
תגובות, תלונות, הערות והארות לכתובת: karmimo@netvision.net.il. 2
מדריך זה דורש ידע קודם והכרות עם שורת בפקודה במערכת הפעלה גנו לינוקס וידע כללי בתכנות.
מדריך זה לא בא להיות מבוא לתכנות אך הוא מציג גישות תיכנותיות לתכנות בבש והמון דוגמאות קוד.
#!/bin/bash echo Hello Worldהסקריפט מכיל שתי שורות בלבד בשורה הראשונה של הסקריפט מגדירים לו עם איזה תכנה להריץ את הסקריפט בשורה השניה מורים לו לכתוב שלום עולם במסך המסוף כפלט. תוכנות מסוף ושורת פקודה לדוגמא טרמינל וקונסול. הרצת הקובץ הנ”ל אחרי שמירה שלו בשם
./hello.sh
#!/bin/bash tar -cZf /var/my-backup.tgz /home/me/
סקריפט זה מגבה את כל תקיית הבית לתוך קובץ מכווץ TGZ סקריפט גיבוי טוב יותר יהיה בהמשך.
ישנם 3 מאפיינים שאפשר לנתב : stdin, stdout, stderr
std = standard
stdin = standard input לנתב קלט
stdout = standard output לנתב פלט
stderr = standard error לנתב שגיאות ופלט השגיאה
בעיקרון ניתן לנתב:
1. stdout לקובץ
2. stderr לתוך קובץ
3. stderr לתוך stdout
4. stdout לתוך stderr
5. stderr עם stdout לתוך קובץ
6. stderr עם stdout לתוך stdout
7. stdout עם stderr לתוך stderr
מוסכמות
המספר 0 מייצג את הקלט stdin
המספר 1 מייצג את הפלט stdout מה שנכתב במסך
המספר 2 מייצג את פלט השגיאה stderr שזה גם טקסט שנשמר בבפר אבל לא מוצג תמיד במסך ולכן הוא נפרד מהפלט הסטנדרטי והוא בבפר שכל פעם נמחק ומתחדש בהתאם לשגיאות הפקודה האחרונה אם יש כאלה.
האופרטורים שמנתבים הם
מנתב לקובץ חדש ומאפס אותו
מנתב לקובץ ומוסיף את הנתונים בסופו
&1 מנתב לפלט 1 כלומר למסך
&2 מנתב לפלט השגיאה כלומר ל 2 stderr
בדוגמא זו יראו איך לנתב את פלט המסך לתוך קובץ שנקרא לו ls-l.txt
קובץ זה יכיל את הטקסט שרואים במסך כאשר מקישים את הפקודה ls -l
ls -l > ls-l.txt
בדוגמא זו רואים את פלט השגיאה של תכנית מסויימת שמריצים grep מנותב
לקובץ שקראנו לו grep-errors.txt
grep da * 2> grep-errors.txt
כאן רואים שפלט המסך של הפקודה המורצת שמיוצג על ידי הסיפרה 1 מנותב עם אופרטור הניתוב <&
ל 2 שהוא מציין את פלט השגיאות.
grep da * 1>&2
grep * 2>&1
לשים לב לאופרטור &< שמנתב גם את הפלט של המסך וגם את פלט השגיאה לקובץ המרוקן
שימוש זה נעשה אם רוצים שפקודה תרוץ ולא יראו שום פלט שלה גם כאשר יש שגיאה.
rm -f $(find / -name core) &> /dev/null
צינורות זה דרך להעביר את פלט המסך של תכנית אחת כקלט לתכנית אחרת
אופרטור הצינורה סימנו - “|” כאשר מימינו ומשמאלה יש את התכנית שפולטת
והתכנית שמקבלת את הפלט של התכנית הקודמת משמאל לימין.
כאן דוגמא לפקודה ls שהפלט שלה אינו מודפס במסך ובמקום זאת מנותב לתכנית
אחרת שמשתמשת בפלט ומוציאה פלט אחר שהוא זה שיודפס במסך.
ls -l | sed -e "s/[aeio]/u/g"
ls -l | grep "\.txt$"
ניתן להשתמש במשתנים כמו בכל שפת תכנות העניין הוא שאין טיפוסי משתנים.
משתנה בבש יכול לקבל מספר תו טקסט. אין שום צורך בהצהרת טיפוס משתנה הטיפוס נקבע על פי הערך המוכנס.
שורה 2 יוצרת משתנה בשם STR ומכניסה את הטקסט “Hello World” אליו. לאחר מכן הערך של משתנה זה נגיש באמצעות השמת הסימן דולר $ לפניו.
שים לב שאלמלא השתמשנו בסימן דולר פלט התכנית היה שונה ובמקום שהתכנה תכתוב את הטקסט שלום עולם היא תכתוב STR כפלט טקסט.
#!/bin/bash STR="Hello World!" echo $STR
#!/bin/bash OF=/var/my-backup-$(date +%Y%m%d).tgz tar -cZf $OF /home/me/
סקריפט זה מציג עניין נוסף. קודם כל אתה צריך לזהות את יצירת המשתנה והסימן שלו בשורה 2. שים לב לביטוי $(date +%Y%m%d).tgz.
אם אתה מריץ את הסקריפט תוכל לשים לב שהוא מריץ את הפקודה קודם ומשתמש בפלט שלה כערך למשתנה.
תוכל לבחון זאת באמצעות הדוגמאות הבאות שימחישו את ההבדלים:
echo ls
בדוגמא הנ”ל הפלט הוא ls
echo $(ls)
בדוגמא הנ”ל הפלט הוא הטקסט של הפקודה ls כפי שמקישים אותה במסוף.
משתנים מקומיים יוצרים על ידי מילת המפתח local לפני שם המשתנה… דוגמא זו מדגימה זאת:
#!/bin/bash HELLO=Hello function hello { local HELLO=World echo $HELLO } echo $HELLO hello echo $HELLO
התניות מאפשרות להחליט אם לבצע פעולה או לא באמצעות הצבת תנאים.
התניות בעלות הרבה צורות. הצורה הבסיסית ביותר היא:
אם [ תנאי שמתקיים ]
אז [ פעולות שיתבצעו בהתאם לתנאי ].
תנאים או התניות יכולים להיות גם בעלי מבנה משפט התניה אחר כמו
אם [ תנאי שמתקיים ]
אז [פעולות שיבוצעו ]
אחרת [ פעולות אחרות שיבוצעו ]
אפשרות נוספת היא שרשור של כל מיני התניות לביצוע פעולה שנבדקות בסדר מסויים כגון:
אם [ תנאי שמתקיים ]
אז [פעולות שיבוצעו ]
אחרת אם [ תנאי אחר שמתקיים ]
אז [ פעולות אחרות שיבוצעו ]
אחרת אם [ תנאי אחר שמתקיים ]
אז [ פעולות אחרות שיבוצעו ]
אחרת [ פעולות אחרות שיבוצעו ]
בבש תחביר הפקודה יראה בצורה זו:
#example 1: if [ התנייה ]; then doSomthing; fi;
#example2: if [ התניה ] then doSomthing else doSomethingElse fi
ככלי התחביר כאשר כותבים את משפט ההתניה בשורה אחת יש להפריד מילות מפתח ופקודות עם נקודה פסיק כמו בדוגמא הראשונה. אם רוצים לכתוב את משפט ההתניה שלא בשורה אחת אז לא צריך נקודה פסיק בכלל.
#!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true fi
אם ההתניה בתוך הסוגריים המרובעים היא אמת אז יודפס הביטוי. הפקודה להדפסה צריכה להימצא תמיד אחרי המילה then ולפני המילה fi שהיא finish מסמנת את הסוף של ההתניה.
#!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true else echo expression evaluated as false fi
#!/bin/bash T1="foo" T2="bar" if [ "$T1" = "$T2" ]; then echo expression evaluated as true else echo expression evaluated as false fi
בפרק זה נדון בלולאות for, while, until
לולאת FOR שונה במקצת מאשר בשפות תכנות אחרות. בעיקרון לולאה זו מאפשרת להריץ מספר פעולות בצורה מחזורית.
לולאת ה WHILE מבצעת קוד אם מתקיימת התנייה כלשהי ועוצרת כאשר ההתניה לא מתקיימת עוד או שאחת הפקודות
בתוך הלולאה היא break שזו פקודה ליציאה לחלוטין ממחזוריות הפקודות הרצות של הלולאה.
לולאת UNTIL כמעט זהה ל WHILE בהבדל שסדרת הפקודות בתוכה מתבצעת פעם אחת בטרם נבדק התנאי להמשך
מחזוריות ביצוע סדרת הפקודות שבתוך הלולאה.
#!/bin/bash for i in $( ls ); do echo item: $i done
בשורה השניה הגדרנו את “i” למשתנה שלוקח ערכים שונים הקיימים בתוך פלט הפקודה ls. השורה השלשית יכולה להיות ארוכה יותר ואפשר גם להכניס פקודות נוספות אחריה ולפני ה done אם צריך.
#!/bin/bash for i in `seq 1 10`; do echo $i done
#!/bin/bash COUNTER=0 while [ $COUNTER -lt 10 ]; do echo The counter is $COUNTER let COUNTER=COUNTER+1 done
#!/bin/bash COUNTER=20 until [ $COUNTER -lt 10 ]; do echo COUNTER $COUNTER let COUNTER-=1 done
כמעט בכל שפות התיכנות ניתן להשתמש בפונקציות לאגד קבוצת פקודות קוד תחת שם אחד ולהשתמש ברקורסיות. הצהרה על פונקציה נעשית בצורה של function my_func {my code קריאה לפונקציה נעשית באמצעות קריאה בשמה.
#!/bin/bash function quit { exit } function hello { echo Hello! } hello quit echo foo
שורות 2-4 מכילות את הפונקציה “quit”. שורות 5-7 מכילות את הפונקציה “hello”. אם אינך בטוח מה הפונקציות עושות נא לנסות! שים לב שסדר הצהרת הפוקציות לא משמעותי בתכנית שקודם קוראת לפונקציה השניה ואז לראשונה.
#!/bin/bash function quit { exit } function e { echo $1 } e Hello e World quit echo foo
סקריפט זה כמעט וזהה לקודם. ההבדל בפוקציה זו שהיא מדפיסה את הערך הראשון שהיא מקבלת $1 (ארגומנט). ארגומנטים בפונקציות מתנהגים כמו ארגומנטים שניתנים לסקריפט.
#!/bin/bash OPTIONS="Hello Quit" select opt in $OPTIONS; do if [ "$opt" = "Quit" ]; then echo done exit elif [ "$opt" = "Hello" ]; then echo Hello World else clear echo bad option fi done
אם תריץ סקריפט זה תראה שזה חלום של כל מתכנת לתפריטים מבוססי טקסט. תוכל כנראה להבחין שזה מאוד דומה למבנה לולאת FOR.
#!/bin/bash if [ -z "$1" ]; then echo usage: $0 directory exit fi SRCD=$1 TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCD
הביטוי במשפט התניה הראשון בוחן אם התכנית מקבלת איזה שהם ארגומנטים או ערכים ויוצאת כשאין ערכים תוך כדי שהיא
מראה למשתמש הודעה איך להשתמש בסקריפט. שאר הסקריפט אמור להיות ברור.
בהרבה מקרים תרצה לקבל קלט מהמשתמש וישנם מספר דרכים להשיג זאת. זו אחת הדרכים:
#!/bin/bash echo Please, enter your name read NAME echo "Hi $NAME!
אפשרות נוספת היא לקבל מספר ערכים באמצעות read דוגמא זאת תמחיש את הכוונה:
#!/bin/bash echo Please, enter your firstname and lastname read FN LN echo "Hi! $LN, $FN !"
נסה זאת בשורת הפקודה:
echo 1 + 1
אם ציפית לראות 2 אז אתה תתאכזב. מה אם אתה רוצה לבצע פעולות עם מספרים בבש? הפתרון הוא כזה:
echo $((1+1))
זה יבצע פלט קצת יותר הגיוני. הינך יכול להשיג זאת גם בצורה זאת:
echo $[1+1]
אם אתה צריך להשתמש ביותר פעולות חשבוניות תצטרך להשתמש ב bc לבצע ביטויים חשבוניים. למשל אם תריץ echo $[3/4] t בשורת הפקודה תקבל 0 ולא 0.75 משום שהבש משתמש רק במספרים שלמים. על מנת לקבל תוצאה של 0.75 תצטרך לעשות זאת בצורה זאת:
echo 3/4|bc -l
בדרך כלל תמיד תשתמש ב #!/bin/bash להלן דוגמאות איך למצוא היכן בש ממוקם אצלך.
locate bash
לא בכל המכונות יש את הפקודה הנ”ל.
find ./ -name bash
את הפקודה הנ”ל לבצע מספריית השורש /
אפשרויות מיקום נוספים שאפשר לבדוק:
ls -l /bin/bash ls -l /sbin/bash ls -l /usr/local/bin/bash ls -l /usr/bin/bash ls -l /usr/sbin/bash ls -l /usr/local/sbin/bash
אפשר גם לנסות עם הפקודה which
which bash
בבש, הערך המוחזר מתכנית מאוחסן במשתנה מיוחד ששמו דולר סימן שאלה $?…
דוגמא זו תמחיש כיצד לתפוס ערך מוחזר מתכנית בנניח שהספריה dada לא קיימת:
#!/bin/bash cd /dada &> /dev/null echo rv: $? cd $(pwd) &> /dev/null echo rv: $?
סקריפט זה מראה את כל הטבלאות מכל בסיסי הנתונים בהנחה שיש לך mySQL מותקן. בכל אופן לקחת בחשבון לתת שם משתמש וסיסמא תקפים.
#!/bin/bash DBS=`mysql -uroot -e"show databases"` for b in $DBS ; do mysql -uroot -e"show tables from $b" done
ניתן להשתמש במספר קבצים בתוך קוד הפקודות.
(1) s1 = s2 זהה ושווה
(2) s1 != s2 שונה
(3) s1 < s2 קטן גדול
(4) s1 > s2 קטן גדול
(5) -n s1 לא ערך ריק
(6) -z s1 ערך ריק מתוכן
השוואת שתי מחרוזות טקסט
#!/bin/bash S1='string' S2='String' if [ $S1=$S2 ]; then echo "S1('$S1') is not equal to S2('$S2')" fi if [ $S1=$S1 ]; then echo "S1('$S1') is equal to S1('$S1')" fi
הערה: כדאי מאוד בקוד לשים מרכאות סביב המשתנים ולכתוב “$S1”=“$S2” משום שאם המשתנים לא מכילים ערך תתקבל שגיאת תחביר ולכן שימוש במרכאות סביב שמות המשתנים בהתניות הינו דבר כדאי.
חיבור + חיסור - כפל * חילוק ומתקבלת תוצאה ללא השארית / חילוק כאשר רוצים כתוצאה את השארית בלבד %
-lt (<)
-gt (>)
-le (⇐)
-ge (>
-eq (=
-ne (!
חלק מפקודות אלה כוללים כללי תחביר ברמה של שפת תיכנות. מפקודות אלה רק הדברים הבסיסיים יוסברו.
להסבר מעמיק יותר תוכל להסתכל בדפי ה man של פקודות אלה.
ניתן להשתמש בו לעריכת קבצים לבצע בהם שינויים וגם כפילטר למציאת נתונים בתוך קובץ או לבצע שינויים בעלי כללי תחביר בתוך קובץ על פי קריטריונים מסויימים.
$sed 's/to_be_replaced/replaced/g' /tmp/dummy
העורך sed בדוגמא הנ”ל מחליף את הטקסט to be replaced בטקסט replaced כאשר טקסט זה מופיע מהקובץ הנקרא tmp/dummy ופולט את התוצאה. כמו כן ניתן לנתב את הפלט לקובץ אחר.
$sed 12, 18d /tmp/dummy
כאן הוא פולט את כל תוכן הקובץ מלבד שורות 12 עד 18. הקובץ המקורי לא נפגע או משתנה באמצעות פקודה זו.
העיקרון כאן הוא דומה: נניח שיש לנו קובץ שזה תוכנו
“test123
test
tteesstt”
תתבצע הפקודה
$awk '/test/ {print}' /tmp/dummy
test123
test
בנ”ל התבנית ש awk מחפש היא “test” והפקודה שמתבצעת כאשר הוא מוצא שורה בקובץ עם המחרוזת הזאת זה print.
$awk '/test/ {i=i+1} END {print i}' /tmp/dummy
3
כאשר אתה מחפש תבניות טקסט בקובץ אתה צריך להכניס את הטקסט לקובץ בין המרכאות עם כדי שתוכל להניח את כל התבניות והפעולות לקובץ.
$grep "look for this" /var/log/messages -c
12
המחרוזת “look for this” נמצאה למשל 12 פעמים בקובץ /var/log/messages.
בדוגמא הבאה נראה שהפלט לא כפי שציפינו. קובץ הדמה שנשתמש לדוגמא זו מכיל את הטקסט הבא: “bash introduction hoto test file”
$wc --words --lines --bytes /tmp/dummy
2 5 34 /tmp/dummy
הכלי wc פולט את הנתונים שלו בצורה סטנדרטית והפרמטרים מציגים את הסדר בהתאמה של הפלט.
נניח שיש לנו קובץ שזה תוכנו:
” ב
ג
א”
$sort /tmp/dummy
וזה יהיה הפלט
א
ב
ג
$bc -q
1 == 5
0
0.05 == 0.05
1
5 != 5
0
2 ^ 8
256
sqrt(9)
3
while (i != 9) {
i = i + 1;
print i
}
123456789
quit
דוגמא:
$tput cup 10 4
שורת הפקודה מופיעה במיקום (y10,x4)
דוגמא נוספת:
$tput reset
מנקה את המסך
דוגמא נוספת:
$tput cols 80
מראה את מספר התוים שניתן לשים בכיוון x.
מאוד רצוי להכיר את התכניות האלה. ישנם המון תוכנות כאלה שיכולות לעזור לעשות ניסים בשורת הפקודה בבש.
#!/bin/bash SRCD="/home/" TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCD
#!/bin/sh # renna: rename multiple files according to several rules # written by felix hudson Jan - 2000 #first check for the various 'modes' that this program has #if the first ($1) condition matches then we execute that portion of the #program and then exit # check for the prefix condition if [ $1 = p ]; then #we now get rid of the mode ($1) variable and prefix ($2) prefix=$2 ; shift ; shift # a quick check to see if any files were given # if none then its better not to do anything than rename some non-existent # files!! if [$1 = ]; then echo "no files given" exit 0 fi # this for loop iterates through all of the files that we gave the program # it does one rename per file given for file in $* do mv ${file} $prefix$file done #we now exit the program exit 0 fi # check for a suffix rename # the rest of this part is virtually identical to the previous section # please see those notes if [ $1 = s ]; then suffix=$2 ; shift ; shift if [$1 = ]; then echo "no files given" exit 0 fi for file in $* do mv ${file} $file$suffix done exit 0 fi # check for the replacement rename if [ $1 = r ]; then shift # i included this bit as to not damage any files if the user does not specify # anything to be done # just a safety measure if [ $# -lt 3 ] ; then echo "usage: renna r [expression] [replacement] files... " exit 0 fi # remove other information OLD=$1 ; NEW=$2 ; shift ; shift # this for loop iterates through all of the files that we give the program # it does one rename per file given using the program 'sed' # this is a sinple command line program that parses standard input and # replaces a set expression with a give string # here we pass it the file name ( as standard input) and replace the nessesary # text for file in $* do new=`echo ${file} | sed s/${OLD}/${NEW}/g` mv ${file} $new done exit 0 fi # if we have reached here then nothing proper was passed to the program # so we tell the user how to use it echo "usage;" echo " renna p [prefix] files.." echo " renna s [suffix] files.." echo " renna r [expression] [replacement] files.." exit 0 # done!
#!/bin/bash # renames.sh # basic file renamer criteria=$1 re_match=$2 replace=$3 for i in $( ls *$criteria* ); do src=$i tgt=$(echo $i | sed -e "s/$re_match/$replace/") mv $src $tgt done
כדאי להוסיף בתחילת הקוד את השורה
#!/bin/bash -x
דבר זה יכול להציג מידע חשוב על הפלט ולעזור בדיבאגינג