לקבל שמות קבצים עם
לעיתים בעבודה על קבצים אנו שומרים את הנתיב המלא אל הקובץ יחד עם שמו, במידה ונרצה לקבל רק את שמות הקבצים ועדין לקבל קוד יחסית קריא נוכל להשתמש בפקודות
/
ואני מעוניין לקבל רק את שם הקובץ
זוהי הפקודה אותה אריץ ולהלן ההסבר.
היות ובסקריפט אני לא ידע מהו אורך הנתיב בכדי שאדע מהו מספר האינדקס בו אצטרך להשתמש אנו הופכים את המחרוזת כך ששם הקובץ יהיה בתחילת המחרוזת ולא בסופה בשביל כך נשתמש בפקודת rev
זה נראה כך
$
כעת נוכל לבחור את שם הקובץ על ידי בחירת האינדקס הראשון, כרגע יש לנו את הקובץ רק במהופך, בשביל להישאר עם טקסט קריא אז rev שוב.
אז כן יש כאן מספר תהליכים אבל היי זה עדיף מ regex
cut
לעיתים בעבודה על קבצים אנו שומרים את הנתיב המלא אל הקובץ יחד עם שמו, במידה ונרצה לקבל רק את שמות הקבצים ועדין לקבל קוד יחסית קריא נוכל להשתמש בפקודות
cut
וrev
לדוגמה הנתיב אל הקובץ שלי הוא זה/
tmp/some file.py
ואני מעוניין לקבל רק את שם הקובץ
זוהי הפקודה אותה אריץ ולהלן ההסבר.
echo " /tmp/some file.py" | rev | cut -d/ -f1 | rev
הפקודה cut
חותכת טקסט לפי תו שמגדירים לה עם d-
במקרה שלנו מדובר על תו /,
אנו בוחרים איזה "איבר" להציג על ידי דגל f- ואת האינדקס אותו אנו רוצים, f1-
יבחר את המילה הראשונה שלפני ה/ f2-
יבחר את השניה וכן על זה הדרך.היות ובסקריפט אני לא ידע מהו אורך הנתיב בכדי שאדע מהו מספר האינדקס בו אצטרך להשתמש אנו הופכים את המחרוזת כך ששם הקובץ יהיה בתחילת המחרוזת ולא בסופה בשביל כך נשתמש בפקודת rev
זה נראה כך
$
echo "/tmp/some file.py" | rev
yp.elif emos/pmt/
כמו שאפשר לראות שם הקובץ נמצא כעת בתחילת המחרוזת, אומנם כל הטקסט התהפך לגמרי אבל החלק החשוב הוא שהתו שאנו רוצים לפצל על פיו נשאר אותו הדבר.כעת נוכל לבחור את שם הקובץ על ידי בחירת האינדקס הראשון, כרגע יש לנו את הקובץ רק במהופך, בשביל להישאר עם טקסט קריא אז rev שוב.
אז כן יש כאן מספר תהליכים אבל היי זה עדיף מ regex
להישאר מוגנים תמיד עם גרשיים כפולות
אם יש משהו שמשתמשי
הבעיה שבדרך כלל נתקלים בה היא שמות קבצים עם רווחים, באש מתחשבת ברווחים כהפרדה בין ארגומנטים, לדוגמה כשאנו מריצים פקודה ומעבירים לה פרמטרים ברירת המחדל של
אבל מה זה לעזאזל
המשתנה שאחראי במערכת על הערך שמפריד בין פרמטרים קרוי
אם יש משהו שמשתמשי
Bash
חייבים לזכור הוא שלעולם אבל לעולם לא פונים למשתנה רק עם שמו, תמיד לעטוף אותו "בגרשיים כפולות".הבעיה שבדרך כלל נתקלים בה היא שמות קבצים עם רווחים, באש מתחשבת ברווחים כהפרדה בין ארגומנטים, לדוגמה כשאנו מריצים פקודה ומעבירים לה פרמטרים ברירת המחדל של
IFS
היא רווח ולכן כל מילה היא ארגומנט בפני עצמה.אבל מה זה לעזאזל
IFS
?המשתנה שאחראי במערכת על הערך שמפריד בין פרמטרים קרוי
IFS
שהוא קיצור של internal field separator, ברירת מחדל של הערך הוא רווח, טאב ושורה חדשה (דבר שאפשר לשנות כמו כל השמה למשתנה).$ printf %s "$IFS" | od -vtc -to1
0000000 \t \n
040 011 012
0000003
ברגע שמעבירים משתנה שמכיל שם כלשהו הכולל רווחים מה שקורה הוא שבאש פונה אל IFS
לוקח ממנו את הערך שבפנים ומחלק איתו את המחרוזת, ברגע שמשתמשים בגרשיים כפולות סביב המשתנה הערך עובר כערך אחד מלא ולא עובר דרך הIFS
נכון אפשר להגדיר את את IFS
שיפצל על פי שורה חדשה אבל היי למה לא לעבוד קללמצוא עזרה
המילים הראשונות שכל בייבי לינוקס שומע הן לך תקרא תיעוד, לרוב הכוונה להרצה של
הפקודה
פקודה פונקציונלית יותר היא
ישנם פקודות בסיסיות שלא תמצאו להן תיעוד לא עם
@bash_tips
המילים הראשונות שכל בייבי לינוקס שומע הן לך תקרא תיעוד, לרוב הכוונה להרצה של
man command
כלשהו שמציג את ההסבר מה הפקודה יכולה לעשות ואלו דגלים זמינים לה.הפקודה
man
ישנה יחסית והיא מבוססת על פקודות more
| less
שיודעות לקחת טקסט ארוך ולחלק אותו לתצוגה על המסך, ומאפשרת לחפש טקסט על ידי /
וכתיבת הביטוי החיפוש.פקודה פונקציונלית יותר היא
info
שאפשרת קריאה רציפה יותר של תוכן פתיחה של לינקים ומעבר בין אזורים דומים בתיעוד כך שניתן לקבל תמונה מקיפה יותר על דברים, עוד על הפקודה בתיעוד :-) .ישנם פקודות בסיסיות שלא תמצאו להן תיעוד לא עם
man
ולא עם info
$ man fgהסיבה היא שהן פקודות שהן build in של bash, בשביל לקרוא תיעוד על הפקודות הללו יש את פקודה help שגם היא איך לא פקודה מובנת של bash.
No manual entry for fg
$ help fgפקודות build in הן פקודות שזמינות גם אם המערכת ריקה לגמרי ושום כלי אחר לא זמין לה, בין הפקודות אפשרי למצוא את
fg: fg [job_spec]
Move job to the foreground.
Place the job identified by JOB_SPEC in the foreground, making it the
current job. If JOB_SPEC is not present, the shell's notion of the
current job is used.
Exit Status:
Status of command placed in foreground, or failure if an error occurs.
cd, shopt, fg, history
ועוד, ניתן לראות את הרשימה המלאה על ידי הרצה של help -d
@bash_tips
לסלול נתיב עם
החלק המעניין הוא שאפשר להעביר לפקודה נתיב מרובה סלאשים כפולים והוא ימיר אותו לנתיב תקין, זה נראה כך.
אז כך לא פעם כשכותבים סקריפט שמצפה לקבל קלט נתיב כלשהו מהמשתמש אנו עשויים להיכשל אם יש סלאשים כפולים, כי אנחנו חשבנו שהמשתמש יזין את שם התיקייה בלי סלאש בסוף מה שלא בדיוק קרה וכו', שימוש ב
@bash_tips
readlink
פקודת readlink
שימושית מאוד כשרוצים למצוא את המקור של קובץ סימבולינק מסויים, יש לה מספר פרמטרים מעניינים אבל לא זה מה שחשוב לנו היום.החלק המעניין הוא שאפשר להעביר לפקודה נתיב מרובה סלאשים כפולים והוא ימיר אותו לנתיב תקין, זה נראה כך.
$ readlink -m ///tmp///123//Bswap_HPBake/עכשיו אתם שואלים בטח נו ולמה זה טוב?
/tmp/123/Bswap_HPBake
אז כך לא פעם כשכותבים סקריפט שמצפה לקבל קלט נתיב כלשהו מהמשתמש אנו עשויים להיכשל אם יש סלאשים כפולים, כי אנחנו חשבנו שהמשתמש יזין את שם התיקייה בלי סלאש בסוף מה שלא בדיוק קרה וכו', שימוש ב
readlink
פותר את הבעיה.@bash_tips
לאחד ניתובים עם
כשכותבים סקריפט הרבה פעמים נרצה לשמור שגיאות במידה והן קיימות על ידי ה-redirection
@bash_tips
<&
קונספט שרץ בלינוקס אומר שיש כמה ערוצי תקשורת למכונה 0 = stdin - ערוץ בו עובר הקלט למכונההמיפוי הזה מיוצג הרבה מהמקרים על ידי תווי redirection בצורה הבאה
1 = stdout - ערוץ אליו נשלח הפלט הסטנדרטי
2 = stderr - ערוץ אליו נשלחות השגיאות
< stdin
> stdout
2> stderr
עד כאן הכל ידוע ומוכרכשכותבים סקריפט הרבה פעמים נרצה לשמור שגיאות במידה והן קיימות על ידי ה-redirection
$ ls directory_that_not_exist 2> error.log
או שנרצה שיוצג למסך רק פלט רלוונטי למשימה שלנו ולזרוק לפח את שאר התוכן שמגיע מהstdout
של פקודות אחרות$ ls /tmp/ /directory_that_not_exist > /dev/null
העניין הוא שבדוגמה הזאת אני מפנה רק את הפלט של stdout
אבל פלט של ה-stderr
עדין יוצג, בשביל לתפוס את שני הערוצים יחד ניתן להשתמש ב-<&
.$ ls /tmp/ &> /dev/null
@bash_tips
👍1
עדכון:
1 באפריל שמח לכולם
מי שאהב ורוצה להגיש את זה כהצעה לפיתוח יש עוד פיצ'רים כמו זה שברגע שהפקודה השניה נכשלת אז גם הפקודה הראשונה מבוטלת :-)
הפוסט המקורי
פיצ'ר חדש command brace expansion
פיצ'ר מאוד מוכר ושימושי בבאש מאפשר להעביר מספר פרמטרים לפקודה בפעם אחת על ידי שימוש בסוגריים מסולסלות, לדוגמה
בפעם הראשונה הפרמטר עובר אל הפקודה
@bash_tips
1 באפריל שמח לכולם
מי שאהב ורוצה להגיש את זה כהצעה לפיתוח יש עוד פיצ'רים כמו זה שברגע שהפקודה השניה נכשלת אז גם הפקודה הראשונה מבוטלת :-)
הפוסט המקורי
פיצ'ר חדש command brace expansion
פיצ'ר מאוד מוכר ושימושי בבאש מאפשר להעביר מספר פרמטרים לפקודה בפעם אחת על ידי שימוש בסוגריים מסולסלות, לדוגמה
$ touch deploy_{DEV,PROD}.yml
deploy_DEV.yml
deploy_PROD.yml
לאלו שלא עוקבים אחר הפיתוח של bash פיצ'ר חדש שיכנס בגרסה הבאה של באש מאפשר להריץ מספר פקודות על אותו פרמטר, אותו רעיון כמו מקודם רק על פקודות, זה נראה כך$ {mkdir,cd} my_new_directory
mkdir my_new_directory
cd my_new_directory
מה יש לנו כאןבפעם הראשונה הפרמטר עובר אל הפקודה
mkdir
ואז נוצרת תיקייה, לאחר שנוצרת תיקייה אותו פרמטר מועבר לפקודה cd
.@bash_tips
אפשר להמשיך
דוגמה טובה תמחיש את העניין
@bash_tips
yes
פקודה נחמדה בשם yes
מאפשרת לנו להעביר את הערך yes כשמשתמשים בפקודות ששואלות את המשתמש אם הוא מאשר משהו או לא [Y/N]דוגמה טובה תמחיש את העניין
$ yes | sudo apt-get install ffmpeg
בשלב הזה שהפקודה ממתינה לקלט מהמשתמש יועבר לה הערך yes
After this operation, 2,489 kB of additional disk space will be used.
Do you want to continue? [Y/n]
@bash_tips
לקבל סטטוס הרצה עם
באותה הצורה ניתן לבדוק ערכים של תנאים רק שכאן יחזור מספר שייצג true או false
@bash_tips
?$ echo
כשמריצים פקודות בטרמינל קל לנו לראות אם הפקודה הצליחה או נכשלה, כשאנו מריצים את הפקודה דרך סקריפט ואנו רוצים חיווי אם התהליך שהרצנו הסתיים בהצלחה אפשרי להשתמש ב echo ?$
ולפי זה לקבוע את המשך ריצת הסקריפט$ ls /opt/
brother containerd CyberGame
$ echo $?
0
$ ls /aaa
ls: cannot access '/aaa': No such file or directory
$ echo $?
2
סטטוס 0 מייצג תוכנית שהסתיימה בהצלחה סטטוס 1 או 2 מייצגים תוכנית שנכשלה, סטטוס ריק מייצג תוכנית שלא רצה.באותה הצורה ניתן לבדוק ערכים של תנאים רק שכאן יחזור מספר שייצג true או false
$ [ 44 -eq 44 ]; echo $?
0
$ [ 44 -eq 60 ]; echo $?
1
@bash_tips
קיצורים ל-Default values
באש מאפשרת לבצע פעולות ובדיקות על משתנים כמו גם לאתחל משתנים לערכי ברירת מחדל עם משפטי קוד קצרים
המשתנה שלי
@bash_tips
באש מאפשרת לבצע פעולות ובדיקות על משתנים כמו גם לאתחל משתנים לערכי ברירת מחדל עם משפטי קוד קצרים
המשתנה שלי
exist_variable="some text here"
תעביר את הפלט 123 אם המשתנה לא קיים$ echo ${not_exist_variable:-123}
123
תעביר את הערך 123 אם המשתנה כן קיים$ echo ${exist_variable:+123}
123
השמת ערך למשתנה אם הוא לא קיים$ echo ${exist_variable:=123}
some text here
$ echo ${not_exist_variable:=123}
123
מחזיר ערך כשגיאה אם המשתנה לא קיים$ echo ${not_exist_variable:?"variable not set"}
bash: not_exist_variable: variable not set
@bash_tips
עבודה עם עם לולאות
ישנם מספר סוגי לולאות בבאש לכל אחת תפקיד שונה
בשביל לרוץ על קבצים ולבצע עליהם פעולה כלשהי אפשר להשתמש בפקודה
@bash_tips
ישנם מספר סוגי לולאות בבאש לכל אחת תפקיד שונה
בשביל לרוץ על קבצים ולבצע עליהם פעולה כלשהי אפשר להשתמש בפקודה
find
אבל אפשר להשתמש בלולאה שמקבלת את הקבצים כאיטרטורfor i in /projects/*.py; do
echo $i
done
החלטתם להשתמש בלולאה על תוכן שצריך להיות מחולק לפי שורות? אולי תרצו להשתמש בלולאת raed whilecat lorem.txt | while read line; do
echo $line
done
סתם יש לכם טווח שאתם רוצים לרוץ עליו?for i in {1..100}; do
echo "Number $i"
done
אההה עם דילוגיםfor i in {1..100..5}; do
echo "steps $i"
done
כן יש גם את לולאה נוסח C likefor ((i = 0 ; i < 100 ; i++)); do
echo $i
done
ובשביל הכיף אפשר להעביר אורך של משתנה כמספר עם {mystring#}$
ולעשות אנימציה מגניבהmystring="some text here "
for ((i=0; i < ${#mystring}; i++)); do
echo -en "${mystring::i} \r"
sleep 0.2
done
@bash_tips
לעבוד במיקום יחסי עם
בשביל לקבל את תיקיית הסקריפט ולא משנה מאיפה הסקריפט מורץ אפשר להשתמש בפקודה
@bash_tips
dirname
כתבנו סקריפט הוא מדהים והוא רץ, הנה כך הוא נראה$ cat test.sh
#!/bin/bash
touch new_file.txt
נריץ אותו/tmp/project $ bash test.sh
/tmp/project $ ls
new_file.txt test.sh
אז נהדר הכל טוב אנחנו בתיקיית הסקריפט ונוצר לנו קובץ חדש בתיקיית הסקריפט, מקסים. מה קורה אם אנסה להריץ את את הסקריפט ממיקום אחר?/tmp/project $ cd /tmp/other_project
/tmp/other_project $ bash /tmp/project/test.sh
/tmp/other_project $ ls
new_file.txt
הופ בעיה, הקובץ שנוצר נמצא בתיקייה שהרצתי (other_project
) ולא בתיקיית הפרויקט (project
) שימוש בפקודת pwd
גם לא יעזור כי גם הוא מחזיר את המיקום הנוכחי ממנו אני מריץ את הסקריפטבשביל לקבל את תיקיית הסקריפט ולא משנה מאיפה הסקריפט מורץ אפשר להשתמש בפקודה
dirname
ובשרשור הפרמטר הראשון שסקריפט שרץ מקבל הלא הוא שמו 0$
$(dirname $0)
אז כרגע כך נראה הקובץ שלנו/tmp/other_project $ cat /tmp/project/test.sh
#!/bin/bash
touch "$(dirname $0)/new_file.txt"
ונריץ/tmp/other_project $ bash /tmp/project/test.sh
/tmp/other_project $ ls
/tmp/other_project $ ls /tmp/project
new_file.txt test.sh
יהייי עובד, הקובץ נוצר בתיקיית הפרויקט לא משנה מהיכן אריץ את הסקריפט.@bash_tips
תודה לכל המפרגנים
חברים שרוצים לשלוח טיפים מוזמנים לשלוח בקבוצה עם האשתג #bashtips
פייתון ושפות סקריפטים
למידה ועזרה בפייתון ושאר סקריפטים
https://t.me/python_he
חברים שרוצים לשלוח טיפים מוזמנים לשלוח בקבוצה עם האשתג #bashtips
פייתון ושפות סקריפטים
למידה ועזרה בפייתון ושאר סקריפטים
https://t.me/python_he
לרוץ מקבילי בבאש עם parallel
באש בנויה בצורה שהיא מריצה קוד שורה אחר שורה ולא מריצה תהליכים בצורה מקבילית, לדוגמה אם יש לי רשימה של שירים שצריך להמיר הפקודה שממירה תרוץ כל פעם ותמיר שיר בודד במקום לנצל את כל יכולות המחשב ולעבוד מקבילית (בהנחה שכלי ההמרה עצמו לא עובד מקבילי)
בשביל להתגבר על הבעיה הזאת יצרו את הכלי הנחמד
נהדר, אז איך זה עובד
הפקודה מקבלת פלט שמשורשר אליה ומפעילה אותו כתהליך, {} בעצם מסמל את הערך הנוכחי שכרגע מוחזק אצל הפקודה, למשל בתהליך שלנו השורה הראשונה נקראת וכרגע אפשרי יהיה להפעיל אליה איזה כלי שאנו רוצים
טוב דוגמה יותר שימושית, יש לי קובץ שמכיל רשימת לינקים להורדת קבצי mp3, אז להוריד אני יודע, משתמשים עם
על המסך לא תראו הרבה דברים מפתיעים אבל אם תסתכלו בתיקייה תראו שמספר קבצים התחילו לרדת במקביל
אותו רעיון אפשרי לעשות עם כיווץ גיבוי או כל דבר שלוקח זמן
טוב זה נחמד אבל איך אני הופך את הסקריפט שלי שירוץ מקבילי
כן זה כל כך פשוט, כל שורה תרוץ בצורה מקבילית ולכן חשוב שנחשוב על זה גם בקוד
אופציות שכדאי להכיר
יש עוד הרבה פיצ'רים לכלי ושווה לקרוא עליו יותר
@bash_tips
באש בנויה בצורה שהיא מריצה קוד שורה אחר שורה ולא מריצה תהליכים בצורה מקבילית, לדוגמה אם יש לי רשימה של שירים שצריך להמיר הפקודה שממירה תרוץ כל פעם ותמיר שיר בודד במקום לנצל את כל יכולות המחשב ולעבוד מקבילית (בהנחה שכלי ההמרה עצמו לא עובד מקבילי)
בשביל להתגבר על הבעיה הזאת יצרו את הכלי הנחמד
parallel
, לא מדובר על כלי באשי שמותקן ברירת מחדל ברוב הפצות הלינוקס והוא שווה בדיקה, אם כןsudo apt install parallel
נהדר, אז איך זה עובד
cat links.txt | parallel {}
הפקודה מקבלת פלט שמשורשר אליה ומפעילה אותו כתהליך, {} בעצם מסמל את הערך הנוכחי שכרגע מוחזק אצל הפקודה, למשל בתהליך שלנו השורה הראשונה נקראת וכרגע אפשרי יהיה להפעיל אליה איזה כלי שאנו רוצים
/tmp$ cat links
https://google.com
/tmp$ cat links | parallel "echo {} \| {}"
https://google.com | https://google.com
טוב דוגמה יותר שימושית, יש לי קובץ שמכיל רשימת לינקים להורדת קבצי mp3, אז להוריד אני יודע, משתמשים עם
wget
יאללה נראה איך זה רץcat links | parallel wget {}
על המסך לא תראו הרבה דברים מפתיעים אבל אם תסתכלו בתיקייה תראו שמספר קבצים התחילו לרדת במקביל
אותו רעיון אפשרי לעשות עם כיווץ גיבוי או כל דבר שלוקח זמן
טוב זה נחמד אבל איך אני הופך את הסקריפט שלי שירוץ מקבילי
parallel -a myscript
כן זה כל כך פשוט, כל שורה תרוץ בצורה מקבילית ולכן חשוב שנחשוב על זה גם בקוד
אופציות שכדאי להכיר
parallel -0
יעביר קלט עם רווחים ותווי בקרה בבטחהparallel -k
ישמור על סדר ההרצהיש עוד הרבה פיצ'רים לכלי ושווה לקרוא עליו יותר
@bash_tips
מילונים / מערכים אסוציאטיבים ובאש
מאז הגרסאות האחרונות של באש יש אובייקט חדש שמוכר מכל שפת תכנות נפוצה הלא הוא מילון/מערך אסוציטיבי, ניתן להגדיר אותו על ידי שימוש ב declare שדיברנו עליו בעבר
הבעיה היא ש-Value יכול להיות רק מספר או מחרוזת אבל לא מערך, במידה ורוצים לשייך מספר ערכים לKey זה לא אפשרי, באש אינה מאפשרת לשייך מערך לKey
ואוו מדהים, מגניב, זה ממש עובד, איזה יופי. רגע מה, רק שניה תעצור הכל
מה בעצם קורה אם אני רוצה מספר מילים כערך אחד?
משהו כזה
אשמח שתשתפו בתגובות
@bash_tips
מאז הגרסאות האחרונות של באש יש אובייקט חדש שמוכר מכל שפת תכנות נפוצה הלא הוא מילון/מערך אסוציטיבי, ניתן להגדיר אותו על ידי שימוש ב declare שדיברנו עליו בעבר
$ declare -A my_dict
$ my_dict=(["First Name"]="Yossi" ["Last Name"]="Cohen")
$ echo ${my_dict[First Name]}
Yossi
$ echo ${my_dict[Last Name]}
Cohen
אחלה הכל עובד יופי טופי, נו אז איפה הבעיה?הבעיה היא ש-Value יכול להיות רק מספר או מחרוזת אבל לא מערך, במידה ורוצים לשייך מספר ערכים לKey זה לא אפשרי, באש אינה מאפשרת לשייך מערך לKey
$ my_dict=(["ListA"]=some text here)
bash: my_dict: text: must use subscript when assigning associative array
bash: my_dict: here: must use subscript when assigning associative array
מי שבכל זאת מתעקש יכול למצוא פתרונות מעניינים הנה אחד מהם
$ my_dict=(["ListA"]="some text here")
$ real_dict=( echo ${my_dict["ListA"]} )
$ echo "${real_dict[0]}"
some
$ echo "${real_dict[1]}"
text
$ echo "${real_dict[2]}"
here
מה היה לנו כאן, יצוא של המחרוזת אותה רצינו שתהיה מערך של ערכים לתוך סוגריים, הסוגריים ממירים את המילים לאיברים בגלל ש IFS מחלק בברירת מחדל על פי רווח והופ יש מערך.ואוו מדהים, מגניב, זה ממש עובד, איזה יופי. רגע מה, רק שניה תעצור הכל
מה בעצם קורה אם אני רוצה מספר מילים כערך אחד?
משהו כזה
[some, text here, more text here]
במקרה כזה נוכל להגדיר חוצץ על פיו נבצע את ההפרדה לאיבריםIFS=","
my_dict=(["ListA"]="some, text here, more text here")
real_dict=(echo ${my_dict["ListA"]})
echo ${real_dict[1]}
echo ${real_dict[2]}
echo ${real_dict[3]}
some
text here
more text here
מה הפתרונות שלכם?אשמח שתשתפו בתגובות
@bash_tips
קסמים ו
אני נמצא באותה תיקייה כשאני מריץ פקודות בטרמינל אני מקבל תוצאה טובה, כשאני מריץ את זה בסקריפט התוצאה היא אחרת, כשננסה לדבג את התוכנית נראה שהוא באמת עשה את הלולאה ועדכן הכל כמו שצריך רק שהערך של המשתנה count_files של התוכנית נשאר כפי שהוא היה בהתחלה
העניין הוא כזה
כשאנו משתמשים בpipe בטרמינל ההרצה של הפקודה מתרחשת באותו סשן ולכן המשתנה count_files שהוא מעלה את ערכו זה אותו משתנה שהכרזנו עליו שורה לפני כן.
כשמריצים את אותו קוד דרך סקריפט ההרצה של הpipe מתרחשת ב subshell ולכן המשתנה באמת מעלה את ערכו רק שהוא לא מעלה את ערכו של המשתנה שמוכרז בתוכנית שלנו אלא על אחר חדש שנושא את אותו השם בדיוק ורץ ב subshell
יש לכם פונקציה מדהימה ואתם עדין רוצים להשתמש בצורת הכתיבה הזאת בסקריפט שלכם? תשתמשו ב
@bash_tips
subshell
תכונה מדהימה יש לבאש שנקראת pipe שמאפשרת לשרשר stdout של פקודה אחת ל stdin של פקודה אחרת לדוגמה פקודת ls מעבירה רשימה לפקודת wc שהיא בתורה סופרת כמה שורות (קבצים או תקיות) יש.$ ls | wc -l
3
כשמנסים להשתמש באפשרות הזאת בכתיבת סקריפטים אנו עשויים לגלות תופעה מעניינת ואפילו קצת מתסכלת, נניח שכך נראה הקוד שלי בסקריפטcount_files=0
ls | while read -r; do (( count_files++ )); done
echo $count_files
בשביל הדוגמה משתנה count_files מאותחל למספר 0 ולאחר מכן אנו מעבירים ללולאה את התוכן של פקודת ls בה כל איטרציה מעלה למשתנה count_files את הערך ב 1, כמו שאנו מכירים עד עכשיו התוצאה של משתנה count_files בסוף הסקריפט אמורה להיות כמספר הקבצים שהעבירה פקודת ls, להלן הרצה של הפקודות בשורת הפקודה ולאחר מכן הרצה דרך סקריפט$ count_files=0
$ ls | while read -r; do ((count_files++)); done
$ echo $count_files
3
$ ./test.sh
0
טוב זה ממש מוזראני נמצא באותה תיקייה כשאני מריץ פקודות בטרמינל אני מקבל תוצאה טובה, כשאני מריץ את זה בסקריפט התוצאה היא אחרת, כשננסה לדבג את התוכנית נראה שהוא באמת עשה את הלולאה ועדכן הכל כמו שצריך רק שהערך של המשתנה count_files של התוכנית נשאר כפי שהוא היה בהתחלה
העניין הוא כזה
כשאנו משתמשים בpipe בטרמינל ההרצה של הפקודה מתרחשת באותו סשן ולכן המשתנה count_files שהוא מעלה את ערכו זה אותו משתנה שהכרזנו עליו שורה לפני כן.
כשמריצים את אותו קוד דרך סקריפט ההרצה של הpipe מתרחשת ב subshell ולכן המשתנה באמת מעלה את ערכו רק שהוא לא מעלה את ערכו של המשתנה שמוכרז בתוכנית שלנו אלא על אחר חדש שנושא את אותו השם בדיוק ורץ ב subshell
יש לכם פונקציה מדהימה ואתם עדין רוצים להשתמש בצורת הכתיבה הזאת בסקריפט שלכם? תשתמשו ב
tmpfile
אבל זה כבר לטיפ אחר@bash_tips
מספר דרכים לדבג סקריפט באש
במידה והסקריפט לא ארוך אפשרי להריץ אותו משורת הפקודה עם דגל -x כך
רק שניה הסקריפט שלי ארוך לאללה ואני רוצה לדבג רק קטע מאוד מסויים, ניתן לעשות זאת על ידי עטיפה של הקטע המבוקש עם הגדרה של
@bash_tips
במידה והסקריפט לא ארוך אפשרי להריץ אותו משורת הפקודה עם דגל -x כך
$ /bin/bash -x myscript.sh
או פשוט להוסיף -x לשאבאנג שבראש הסקריפט שלנו#!/bin/bash -x
כיף נחמד מקסיםרק שניה הסקריפט שלי ארוך לאללה ואני רוצה לדבג רק קטע מאוד מסויים, ניתן לעשות זאת על ידי עטיפה של הקטע המבוקש עם הגדרה של
set -x
בתחילת הקטע וסגירה שלו עם set +x
set -x
bad code
more bad code
set +x
@bash_tips
מלכודות ואירועים
בשביל המצב הזה יש לנו את הכלי
קוד לדוגמה
מופעלת הפונקציה
מה קורה במידה ותוך כדי ריצה של הסקריפט המשתמש לחץ Ctrl+C? במקרה הזה הפונקציה
הנה קוד שתופס את Ctrl+C וישאיר אתכם לכודים בסשן הנוכחי של הטרמינל
בשביל לקבל את רשימת האירועים שזמינים ניתן להריץ את הפקודה
@bash_tips
trap
ו signals
בזמן שאנו כותבים סקריפט נרצה להיות אחראים על הזרימה של הסקריפט, לפעמים הסקריפט נסגר תוך כדי ריצה ואז קבצים שיצרנו או תהליכים שהתחלנו נסגרים בפתאומיות בלא שנוכל לצאת בצורה מסודרת.בשביל המצב הזה יש לנו את הכלי
trap
, מדובר על פקודה שמגיעה כחלק מה bash buildin וכל תפקידה הוא לבצע את הפקודות שמעבירים לה על-פי אירוע (signal) שמתרחש.קוד לדוגמה
clean() {
echo Remove files...
echo Remove Directories...
}
trap clean EXIT
האירוע שיש לנו הוא EXIT
והוא מתרחש מתי שהתוכנית מסיימת לרוץ או אז מופעלת הפונקציה
clean
כדי לסדר יציאה מהסקריפט בצורה מסודרת.מה קורה במידה ותוך כדי ריצה של הסקריפט המשתמש לחץ Ctrl+C? במקרה הזה הפונקציה
clean
לא תרוץ כי האירוע (signal) שהתרחש הוא לא מסוג EXIT
אלא SIGINT
.הנה קוד שתופס את Ctrl+C וישאיר אתכם לכודים בסשן הנוכחי של הטרמינל
HAHA() {
printf "\nsleeping ...zZZ "
}
trap HAHA SIGINT
while true;
do
sleep 10
done
אוקי אבל מאיפה אני משיג את רשימת האירועים שזמינים?בשביל לקבל את רשימת האירועים שזמינים ניתן להריץ את הפקודה
trap -l
$ trap -l
1) SIGHUP 2) SIGINT ...
בשביל לראות פירוט איזה סיגנל נשלח באיזה רמה של המערכת איך הוא נקרא ומתי הוא מתרחש ניתן לקרוא את התיעוד ברמה 7 של סיגנל$ man 7 signal
@bash_tips
לקרוא תיעוד לפי קטגוריות
קטגוריות
את הקטגוריה של דפי התיעוד שמוצגים לנו אפשר לראות על ידי שם הפקודה והמספר שבסוגריים שצמוד אליה, כל קטגוריה מתעדת הקשר אחר של הפקודה, להלן רשימת המספרים והקטגוריות שהן מסמנים מתוך
אפשר לקרוא את כל דפי התיעוד אחד אחרי השני על ידי הרצה של
פקודה נחמדה בהקשר הזה שכדאי להזכיר היא
@bash_tips
man
ו whatis
ראינו שבלינוקס לכל דבר כמעט יש תיעוד והוא זמין על ידי הרצה של man command
, אם נקרא את התיעוד של הפקודה man man
נוכל לראות שהתיעוד בעצם מחולק לפי קטגוריות ובדרך כלל רק קטגוריה אחת מוצגת לנו ולא כולם.קטגוריות
את הקטגוריה של דפי התיעוד שמוצגים לנו אפשר לראות על ידי שם הפקודה והמספר שבסוגריים שצמוד אליה, כל קטגוריה מתעדת הקשר אחר של הפקודה, להלן רשימת המספרים והקטגוריות שהן מסמנים מתוך
man man
1 Executable programs or shell commands
2 System calls (functions provided by the kernel)
3 Library calls
4 Special files (usually found in /dev)
5 File formats and conventions eg /etc/passwd
6 Games
7 Miscellaneous
8 System administration commands
9 Kernel routines [Non standard]
יש סדר לפיו פקודת man מחפשת תיעוד וכשהיא מוצאת היא מציגה את הראשון בלבד, כך שהרצה של פקודת man signal
תעלה תיעוד של הפקודה מהקטגוריה השניה, בשביל לקרוא תיעוד מקטגוריות אחרות ניתן פשוט להריץ את מספר הקטגוריה לפני שם הפקודהman 7 signalכעת נוכל לקרוא תיעוד שקשור לקטגוריה השביעית
אפשר לקרוא את כל דפי התיעוד אחד אחרי השני על ידי הרצה של
man -a command
וכך ברגע שדף התיעוד הראשון נסגר המערכת תציע לנו להמשיך לקרוא את שאר התיעוד מהקטגוריות האחרות שקיימות לאותה פקודהפקודה נחמדה בהקשר הזה שכדאי להזכיר היא
whatis
שמעבר ליכולות הנוספות שלה היא תתן לכם רשימה של דפי העזרה שזמינים לפקודה מאיזה קטגוריה הם והסבר קצר על מהות התיעוד$ whatis signal
signal (7) - overview of signals
signal (2) - ANSI C signal handling
נ.ב. המלצה מאוד חמה לעבור לקרוא את התיעוד של man man@bash_tips