330 likes | 563 Vues
תרגול מס' 7. כתיבת תסריטים ב- C-Shell בקרת תהליכים ב- Unix. תסריטים. הרצת תסריטים מבני בקרה ב- C-Shell דוגמאות השוואה בין C ו- C-Shell. אוטומטיזציה. נניח (מקרה היפותטי לחלוטין) שברשותנו קבצי בדיקה ופלט לתכנית שלנו וברצוננו לבדוק את נכונות התכנית מול קבצים אלו
E N D
תרגול מס' 7 כתיבת תסריטים ב-C-Shell בקרת תהליכים ב-Unix
תסריטים הרצת תסריטים מבני בקרה ב-C-Shell דוגמאות השוואה בין C ו-C-Shell מבוא לתכנות מערכות - 234122
אוטומטיזציה • נניח (מקרה היפותטי לחלוטין) שברשותנו קבצי בדיקה ופלט לתכנית שלנו וברצוננו לבדוק את נכונות התכנית מול קבצים אלו • צריך לכתוב 3 פקודות לכל בדיקה • גם עם שימוש במנגנון ההיסטוריה הרצת הבדיקותמעיקה ולוקחת הרבה זמן מיותר • הפתרון:אוטומטיזציהשל הרצת הפקודות. ניצור קובץ אשר יכיל "תסריט" לפיו יורצו כל הפקודות לפי הסדר > (mtm_checkout< test1.in >! tmpout) >&! tmperr > diff expout1 tmpout > diff experr1 tmperr מבוא לתכנות מערכות - 234122
הרצת תסריט בעזרתsource • ניתן להריץ קובץ המכיל פקודות C-Shell (להלן תסריט - script) על ידי הפקודה source • הפקודות יבוצעו ב-Shell הנוכחי כאילו נכתבו בשורת הפקודה אחת אחרי השניה > source run_tests Running test 1 Running test 2 Running test 3 run_tests echo Running test 1 (mtm_checkout < test1.in >! tmpout) >&! tmperr diff expout1 tmpout diff experr1 tmperr echo Running test 2 (mtm_checkout < test2.in >! tmpout) >&! tmperr diff expout1 tmpout diff experr1 tmperr echo Running test 3 (mtm_checkout < test3.in >! tmpout) >&! tmperr diff expout1 tmpout diff experr1 tmperr מבוא לתכנות מערכות - 234122
תסריטי אתחול • בהתחברות של משתמש למערכת מורץ התסריט .login אשר בתיקית הבית של המשתמש • בפתיחת C-Shell חדש מורץ התסריט .cshrc • הרצות תסריטי האתחול מתבצעות באמצעות source, ולכן הן משפיעות על מצב ה-Shell .cshrc .login set path = ($path .) set savehist=1000 alias llls-l alias cdex2 cd ~mtm/public/1011a/ex2 alias GCC gcc-std=c99 -Wall \ -pedantic-errors -Werror # welcome message echo ------ Welcome `whoami` !-------- echo You are in `pwd` directory of \ `hostname` echo OS is `uname -s` # echo disk usage is `du -sh | cut -f1` echo `who | wc -l` users are logged in echo Today is `date` מבוא לתכנות מערכות - 234122
הרצת תסריט כקובץ הרצה • ניתן להריץ תסריט כפקודה: • בתחילת התסריט יש להוסיף את השורה #!/bin/tcsh -f • #! מסמן ל-Unix שהשורה הראשונה בקובץ מגדירה את התכנית לביצוע שאר הפקודות בקובץ • /bin/tcshהוא שם התכנית לביצוע הפקודות, במקרה שלנו C-Shell • -fמונע מה-Shell להריץ תסריטי אתחול • בנוסף יש להוסיף הרשאת ריצה לקובץ • כעת ניתן להריץ את התסריט כמו תכנית רגילה • בניגוד להרצה באמצעות פקודת source התסריט יבוצע בתהליך Shell חדש אשר יסתיים בסוף ריצת התסריט מבוא לתכנות מערכות - 234122
הרצת תסריט כקובץ הרצה • נמיר את תסריט הרצת הבדיקות שלנו לקובץ הרצה: • בעיה חדשה: התסריטמתחיל להסתבך, הוספתבדיקות נוספות משכפלתקוד בתסריט ולא נוחה (ייתכנו אלפי בדיקות) • פתרון: נשתמש במבני בקרה (תנאים ולולאות) בדומה לשפת C > chmoda+xrun_tests > ./run_tests Running test 1 Running test 2 Running test 3 run_tests #!/bin/tcsh -f echo Running test 1 (mtm_checkout < test1.in >! tmpout) >&! tmperr diff expout1 tmpout diff experr1 tmperr echo Running test 2 (mtm_checkout < test2.in >! tmpout) >&! tmperr diff expout1 tmpout diff experr1 tmperr echo Running test 3 (mtm_checkout < test3.in >! tmpout) >&! tmperr diff expout1 tmpout diff experr1 tmperr מבוא לתכנות מערכות - 234122
while • ניתן ליצור לולאות whileב-C-Shell: while (<expression>)<command1><command2>...end • הפקודות בגוף הלולאה יבוצעו כל עוד <expression> ממשיך להתקיים • לולאות while כמו שאר מבני הבקרה ניתנות לביצוע ישירותמהטרמינל > set i=1 > while ($i <= 3) while? echo $i while? @ i++ while? end 1 2 3 > ./run_tests Running test 1 Running test 2 Running test 3 run_tests #!/bin/tcsh -f set i=1 while ($i <= 3) echo Running test $i (mtm_checkout < test$i.in \ >! tmpout) >&! tmperr diff expout$itmpout diff experr$itmperr end מבוא לתכנות מערכות - 234122
foreach • לולאת foreach משמשת למעבר על רשימה בצורה נוחה foreach <varname> (<list>) <command1> <command2> ...end • <varname> הוא שם המשתנה שיכיל בכל פעם איבר מהרשימה • <list> היא רשימה של מחרוזות run_tests #!/bin/tcsh -f foreach test (test*.in) echo Running $test (mtm_checkout < $test >! tmpout) >&! tmperr diff expout_$test tmpout diff experr_$test tmperr end > ./run_tests Running test1.in Running test2.in Running test3.in מבוא לתכנות מערכות - 234122
if • ניתן להגדיר ב-C-Shell משפטי תנאי בשתי גרסאות • בשורה יחידה ללא else: if (<expression>) <command> • עבור בלוקים של פקודות (עם אפשרות ל-else): if (<expression>) then[commands] [else[more commands] ]endif מבוא לתכנות מערכות - 234122
ביטויים אפשריים ב-if ו-while • השוואת מחרוזותעל ידי == ו-=! • השוואת ערכים מספריים על ידי =>, =<, >, < • אם אחד הפרמטרים לאופרטור אינו ערך מספרי תתקבל שגיאה • התנאי -f <filename>בודק האםקיים קובץ בשם <filename> • התנאי -d <filename>בודק האםקיימת תיקיה בשם <filename> • ניתן להשתמש בסוגריים עבור קדימויות • ניתן להשתמש בפעולות חשבוניות עלערכים מספריים כמו +, -, * ו-/ • ניתן להשתמש באופרטורים לוגייםלהרכבת תנאים ||, && ו-! > set str1=Hello > if ($str1 == Hello) echo yes yes > if (7 < 20) echo yes yes > if (0 == 00) echo yes > if (0 <= 00) echo yes yes >if (-f a.txt) echo file exists > cat > a.txt Hello world! > if (-f a.txt) echo file exists yes > mkdirmtm > if ( !(-f b.txt && -d mtm) ) echo yes yes מבוא לתכנות מערכות - 234122
התאמת מחרוזות • האופרטור ~= מאפשר התאמת מחרוזת לתבנית • הארגומנט השמאלי הוא מחרוזת רגילה • הארגומנט הימני הוא תבנית אשר יכולה לכלול את הסימנים *, ? ו-[ ] כמו שתואר עבור תבניות של שמות קבצים • האופרטור ~! הוא השלילה של ~= > if (ends_with_z =~ *[zZ]) echo match match > if ("no point in string" !~ *.*) echo no point no point > if ("point - . - in string" !~ *.*) echo no point > set file = test4.in > if ($file =~ test*.in) echo test file test file מבוא לתכנות מערכות - 234122
switch • ניתן ליצור מבנה switchב-C-Shell: switch (<string>) case <pattern1>: <cshell commands> [breaksw] ... case <pattern_n>: <cshell commands> [breaksw] [default: <cshell commands>]endsw • הכללים ב-switch מקבלים תבניותוההתאמה מתבצעת כמו עבור אופרטור ~= • אם כמה כללים מתאימים ייבחר הכלל הראשוןשמתאים switch($c) case "[a-z]": echo small letter breaksw case "[A-Z]": echo big letter breaksw case "[0-9]": echo digit breaksw case '$': echo dollar sign breaksw default: echo neither letter nor digit endsw מבוא לתכנות מערכות - 234122
goto • ניתן להשתמש במשפטי goto כמו ב-C goto <label> ... <label>: ... • שימוש במשפטי goto הוא תכנות רע (ללא קשר לשפת התכנות) ולא נהוג להשתמש בהם • השימוש ב-goto יוצר קוד "ספגטי" קשה להבנה מבוא לתכנות מערכות - 234122
העברת פרמטרים בשורת הפקודה • כמו ב-C ניתן לגשת לפרמטרים המועברים בשורת הפקודה לתסריט • המשתנה argvמכיל את רשימת הפרמטרים לתסריט • argv[1] הוא הפרמטר הראשון (ולא שם התסריט) • דרך נוחה יותר לגשת לפרמטר ה-nהיא על ידי $n • $* תוחלף ברשימת כל הפרמטריםלתסריט • $0 תוחלף בשם התסריט echo_script #!/bin/tcsh -f echo command: $0 set number = 1 foreachparam ( $* ) echo parameter $number : $param @ number++ end > echo_scriptaaabbb ccc command: echo_script parameter 1 : aaa parameter 2 : bbb parameter 3 : ccc איך ניתן לדעת את מספר הפרמטרים שהתקבלו? מבוא לתכנות מערכות - 234122
קריאת קלט • ניתן לקרוא שורה מהקלט הסטנדרטי על ידי $< • הביטוי $<יוחלףעל ידי C-Shell בשורת קלט שתיקלט מהקלט הסטנדרטי • בד"כ נשתמש בסוגריים כדי לשמור את כל שורת הקלט > set input=$< Hello > echo $input Hello < set line=$< Hello world > echo $line Hello <set line=($<) Hello world > echo $line Hello world מבוא לתכנות מערכות - 234122
עבודה עם קבצים • לא ניתן ב-C-Shellלכתוב ולקרוא מקבצים בצורה ישירה כמו ב-C • כדי לאפשר עבודה עם קבצים נשתמש בהפניות קלט/פלט ותסריטי עזר > cat > file.txt The dingo ate your baby > main_script The first line is: The dingo ate your baby main_script auxiliary_script #!/bin/tcsh -f cat file.txt | auxiliary_script #!/bin/tcsh -f set line=($<) echo "The first line is:" echo "$line" מבוא לתכנות מערכות - 234122
חלוקה פונקציונלית של תסריטים • מאחר ולא ניתן להגדיר פונקציות ב-C-Shell נחלק את הקוד שלנו לתסריטים שונים בקבצים נפרדים • שימו לב שניתן להשתמש ב-C-Shell כדי לערבב תסריטים עם תכניות שנכתבו בשפות שונות בקלות - מכאן מגיע כוחן של שפות תסריטים - scripting languages • ניתן להעביר פרמטרים לתסריטי עזר • בעזרת פרמטרים בשרות הפקודה • בעזרת pipeline • בעזרת קבצים זמניים • ניתן לקבל ערכים חזרה מתסריטי עזר • בעזרת פלט מתסריט העזר מבוא לתכנות מערכות - 234122
העברת והחזרת ערכים • החזרת ערכים מתסריטי העזר יכולה להתבצע בעזרת • שימוש ב-backticks set result = `helper_script` • העברה לתסריט אחר ב-pipeline helper_script | another_script • דרך קובץ זמני helper_script > temp • העברת פרמטרים לתסריטי עזר יכולה להתבצע בדרכים הבאות • בשורת הפקודה helper_script $arg1 $arg2 • בעזרת pipeline echo $arg1 $arg2 | helper_script • דרך קובץ זמני echo $arg1 $arg2 > temp helper_script < temp מבוא לתכנות מערכות - 234122
דוגמה • נתון קובץ בשם football.txt המכיל נתונים על שערים שהובקעו במשחקי כדורגל • כל שורה בקובץ מציינת שם של שחקן, מספר השערים שהבקיע במשחק שנערך בתאריך מסוים, שם הקבוצה בה הוא שיחק ושם הקבוצה היריבה • ברצוננו לכתוב תסריט בשם player אשר יקבל כפרמטר שם של שחקןוידפיס את כל השורות עבורומהקובץ football.txt ואת סכוםמספר השערים שהבקיע football.txt AlonMiz. 2 23/10/93 Macabi-Haifa Macabi-Tel-Aviv IzakZoh. 1 12/11/93 Macabi-Tel-Aviv Hapoel-Beer-Sheva Ronen Ha. 3 27/12/93 Hapoel-Tel-Aviv Macabi-Tel-AvivReuven A. 2 12/11/93 Macabi-Haifa Hapoel-Tel-Aviv EyalBer. 1 20/11/93 Macabi-Haifa Macabi-Tel-Aviv IzakZoh. 1 12/11/93 Macabi-Tel-Aviv Hapoel-Haifa AlonMiz. 2 26/10/93 Macabi-Haifa Beitar-Jerusalem IzakZoh. 2 12/12/93 Macabi-Tel-Aviv Macabi-Hiafa AlonMiz. 2 23/12/93 Macabi-Haifa Macabi-Pet-Tikva Ronen Ha. 3 27/11/93 Hapoel-Tel-Aviv Macabi-Haifa > player "AlonMiz." AlonMiz. 2 23/10/93 Macabi-Haifa Macabi-Tel-Aviv AlonMiz. 2 26/10/93 Macabi-Haifa Beitar-Jerusalem AlonMiz. 2 23/12/93 Macabi-Haifa Macabi-Pet-Tikva Total number of goals: 6 מבוא לתכנות מערכות - 234122
פתרון • לא נוכל לקרוא את הקובץ football.txtישירות מתוך player ולכן ניעזר בתסריט עזר • ניצור את התסריט calc_total אשר יקבלאת השורות הרלוונטיות לשחקן וידפיס אתהשורות וסכום השערים calc_total #!/bin/tcsh -f set line = ($<) set sum = 0 while ($#line != 0) @ sum = $sum + $line[3] echo $line set line = ($<) end echo "total number of goals: $sum" player התבנית הכללית של עיבוד קלט התבנית הכללית של עיבוד קלט #!/bin/tcsh -f grep "$1" football | calc_total התבנית הכללית של עיבוד קלט מבוא לתכנות מערכות - 234122
דוגמה נוספת • כתבו תסריט לדירוג שחקנים בשם scores אשר יקבל רשימה של שמות שחקנים בשורת הפקודה וידפיס את דירוג השחקנים לפי מספר הגולים שהבקיעו • לכל שחקן יש להדפיס את דירוגו, שמו ומספר הגולים שהבקיע • שני שחקנים שהבקיעו מספר זהה של גולים יקבל דירוג זהה • יש להדפיס את השחקנים ממוינים בדר יורד לפי מספר הגולים שהבקיעו > scores "AlonMiz." "IzakZoh." "Ronen Ha." "Reuven A." 1 AlonMiz. 6 1 Ronen Ha. 6 2 IzakZoh. 4 3 Reuven A. 2 מבוא לתכנות מערכות - 234122
פתרון scores calc_score add_ranks #!/bin/tcsh -f set line = ($<) set place = 1 @ goals = $line[3] while ($#line != 0) if ($goals != $line[3]) then @ place++ @ goals = $line[3] endif echo $place $line set line = ($<) end #!/bin/tcsh -f @ i = 1 while ( $i <= $#argv) grep "$argv[$i]" football.txt | calc_score >>! temp @ i++ end sort -nrk3 temp | add_ranks if (-f temp) rm-f temp #!/bin/tcsh -f set line = ($<) set sum = 0 set player_name = ($line[1-2]) while ($#line != 0) @ sum += $line[3] set line = ($<) end echo $player_name$sum מבוא לתכנות מערכות - 234122
השוואה בין C-Shell ל-C • C-Shell היא שפת תסריטים - scripting language • שפות תסריטים נוספות: Perl, Tcl, Python, Ruby. • C היא שפת תכנות מערכת -system programming language • שפות מערכת נוספות: C++, Java, C#. מבוא לתכנות מערכות - 234122
השוואה בין C-Shell ל-C • יתרונותשל C-Shellעל C: • עבודה נוחה עם מחרוזות ומשתנים • נוח "להדביק" תכניות קיימות • קוד קצרמשמעותית לחלק מהמשימות • אין קומפיילר - לא צריך להכריז על דברים • חסרונותשל C-Shell לעומת C: • אין קומפיילר - אין בדיקותמאפשר באגים מסוכנים • איטית(לעתם פי כמה מאות) • נבחר ב-C-Shell עבור מטלות פשוטות וקצרות שזמן הביצוע שלהן לא קריטי set deposits = ($*) set account_balance = 100 foreach d ($deposits) @ acount_balance=$account_balance + $d end echo $account_balance מבוא לתכנות מערכות - 234122
C-Shell - סיכום • כדי לחסוך ביצוע חוזר וידני של פעולות ניתן ליצור תסריטים המכילים רצף פקודות שימושי ולהריצם בעזרת source או הפיכתם לקובץ הרצה • ב-C-Shell קיימים מבני הבקרה while, foreach, if ו-switch המאפשרים כתיבת קוד מתקדם בתסריטים • ניתן לגשת לפרמטרים לשרות הפקודה של תסריט בדומה לתכנית ב-C • כדי לקרוא מהקלט הסטנדרטי ב-C-Shell נשתמש ב-$< • כדי לקרוא מקובץ ב-C-Shell נשתמש בתסריטי עזר ו-pipeline • כתיבת תסריטים מסתמכת על הדבקת תסריטים ופקודות שונות • נשתמש ב-C-Shell עבור מטלות פשוטות שאינן דורשות חישובים רבים, עבור שאר המטלות נמשיך להשתמש ב-C מבוא לתכנות מערכות - 234122
בקרת תהליכים ב-Unix הרצה בחזית וברקע מבוא לתכנות מערכות - 234122
ריבוי תהליכים ב-Unix • Unix היא מערכת הפעלה התומכת בריבוי תהליכים (multi-tasking) • חובה עבור מערכת שנועדה לשירות מספר רב של משתמשים בו זמנית • תהליך הוא הרצה של תכנית כלשהי • ניתן להריץ תכנית יחידה במספר תהליכים במקביל • בניגוד למנשק גרפי בו השליטה במספר תהליכים אינטואיטיבית עבור עבודה בטרמינל יש להכיר מספר פקודות וקיצורים מבוא לתכנות מערכות - 234122
הרצה בחזית וברקע • Shell יחיד יכול להריץ מספר תהליכים בו זמנית • כל התהליכים מדפיסים את הפלט שלהם לערוצי הפלט הסטנדרטיים • רק תהליך אחד יכול להשתמש בערוץ הקלט הסטנדרטי • תהליך אשר מקושר לערוץ הקלט הסטנדרטי רץ בחזית • כדי להריץ תהליך בחזית יש להפעילו כרגיל > command • כל עוד תהליך רץ בחזית אין גישה ל-prompt ולא ניתן להכניס פקודות חדשות • תהליכים שאינם מקושרים לקלט הסטנדרטי רצים ברקע • ניתן להריץ תהליך ברקע על ידי הוספת & לסוף הפקודה > command & • תהליך יכול להיות גם מושהה, במצב זה התהליך אינו רץ אך מצבו נשמר בזיכרון ונוכל להמשיך את הרצתו כשנרצה מבוא לתכנות מערכות - 234122
בדיקת מצב תהליכים • הפקודה jobs מדפיסה את רשימת התהליכים המורצים תחת ה-Shell > jobs • לכל תהליך מוצג מספר מזהה, מצבו ושם הפקודה • סימן ה-+ מייצג את התהליך "הנוכחי" • תהליך זה יהווה את ברירת המחדל לפקודות טיפול בתהליכים • סימן ה-- מייצג את התהליך הבא בתור להיות + כאשר מספר תהליכים מוציאים פלט בו זמנית הטרמינל מתבלגן במהירות > ./loop 1 & [1] 3692 > Loop 1! ./loop 2 & [2] 3711 Loop 2! Loop 1! loop #!/bin/tcsh-f while ( 1 ) sleep 1 echo Loop $1! end המספר בסוגריים המרובעים הוא מספר התהליך תחת ה-Shell מבוא לתכנות מערכות - 234122
שליחת אותות לתהליך בחזית • כדי לשנות את מצב הריצה של התהליך הרץ בחזית ניתן לשלוח אותות בעזרת קיצורים מהמקלדת • Ctrl+C: הורג (מפסיק) את התהליך אשר בחזית • Ctrl+Z: משהה את התהליך אשר בחזית • התהליך מפסיק לרוץ אך מצבו נשמר מבוא לתכנות מערכות - 234122
פקודות לניהול תהליכים • ניתן להחזיר תהליך לריצה בחזית בעזרת fg > fg [process] • ניתן להחזיר תהליך לריצה ברקע בעזרת bg > bg [process] • ניתן להשהות תהליך על ידי stop > stop <process> • ניתן להרוג תהליך על ידי הפקודה kill > kill <process> • ניתן לציין מספר תהליך ע"י % • ניתן להריץ את התהליך ששמו מתחיל ב-<str> ע"י %<str> • אם קיימים שני תהליכים מתאימים הפקודה תיכשל • עבור fg ו-bg: אם לא צוין מספר תהליך יוחזר התהליך שמסומן ב-+ מבוא לתכנות מערכות - 234122
בקרת תהליכים ב-Unix - סיכום Ctrl+Z ריצה בחזית מושהה הרצה רגילה fg Ctrl+C bg kill stop fg ריצה ברקע מופסק הרצה עם & kill מבוא לתכנות מערכות - 234122