לקבץ פקודות ותת מעטפת
רמת קושי: #advanced
באש מאפשרת לנו לאגוד מספר פעולות יחד ולשרשר את הפלט הסופי רק בגמר הריצה של כל הפקודות ל stdout, בכדי לעשות כך ניתן להריץ את הפקודות בתוך סוגרים / מסולסלות ואת הפלט לשרשר החוצה.
לדוגמה
הקוד הבא לא ישרשר את שני המילים לקובץ test אלא ידפיס אחד למסך ואחד לקובץ המדובר
זה לא משנה אם נשתמש בסוגריים מסולסלות או בסוגריים רגילות, בשני המקרים הפקודות ירוצו בגרופ. העניין הוא ששימוש בסוגריים רגילות הפקודות ירוצו ב
השמה בתת מעטפת
#grouping_commands
@bash_tips
רמת קושי: #advanced
באש מאפשרת לנו לאגוד מספר פעולות יחד ולשרשר את הפלט הסופי רק בגמר הריצה של כל הפקודות ל stdout, בכדי לעשות כך ניתן להריץ את הפקודות בתוך סוגרים / מסולסלות ואת הפלט לשרשר החוצה.
לדוגמה
הקוד הבא לא ישרשר את שני המילים לקובץ test אלא ידפיס אחד למסך ואחד לקובץ המדובר
$ echo first; echo second > /tmp/test
first
$ cat /tmp/test
second
במקום זאת בכדי להפנות את שני הפלטים של פקודת ה echo לקובץ אחד ניתן לתחום את הפקודות כגרופ על ידי סוגריים / מסולסלות, ורק כשהפקודות יסיימו לרוץ הפלט יועבר הלאה.$ (echo first echo second) > /tmp/test
$ cat /tmp/test
first
second
חשוב לשים לבזה לא משנה אם נשתמש בסוגריים מסולסלות או בסוגריים רגילות, בשני המקרים הפקודות ירוצו בגרופ. העניין הוא ששימוש בסוגריים רגילות הפקודות ירוצו ב
subshell
ואילו שימוש בסוגריים מסולסלות יריץ את הפקודות בshell הנוכחיהשמה בתת מעטפת
$ (a=1; b=2; c=3; echo $a-$b-$c)
1-2-3
$ echo $a-$b-$c
--
השמה במעטפת הראשית$ {a=1; b=2; c=3; echo $a-$b-$c}
1-2-3
$ echo $a-$b-$c
1-2-3
#subshell#grouping_commands
נ.ב. ימים אחרונים לאתגר ולאחר מכן אפרסם פתרון אפשרי.@bash_tips
רמת קושי: #advanced
פוסט נהדר של ינון פרק
אני מדביק כאן את הלינק לפוסט כי הדיון בתגובות פשוט מעניין
https://t.me/c/1217780911/5892
#cal
#ncal
@bash_tips
פוסט נהדר של ינון פרק
אני מדביק כאן את הלינק לפוסט כי הדיון בתגובות פשוט מעניין
https://t.me/c/1217780911/5892
#cal
#ncal
@bash_tips
Bash Tips
bash_tips-stop_recursion.sh
רקורסיה $0 $$, ושני אתגרונים בקצה
רמת קושי: #advanced
בעבר דברנו על כך שכשמעבירים לסקריפט באש פרמטרים אז הם נכנסים לסקריפט כמשתנים בשמות $1 לפרמטר הראשון ו$2 $3 לשני והשלישי בהתאמה וכו', מצליחים לנחש איזה ערך נמצא במשתנה $0?
עכשיו המח הקודח שלנו מיד נכנס לפעולה וחושב אה מגניב אז ככה בעצם אפשר ליצור סקריפט שקורא לעצמו. רקורסיה!!!!1 💪💪💪
הסקריפט נח ל20 שניות ומריץ את עצמו שוב
כמו שאתם מבינים החולשות של רקורסיה נמצאות גם כאן, כל התהליכים נוצרים תחת התהליך המקורי, הסקריפט אף פעם לא מגיע לתהליך סיום ולכן כל הרצה שומרת מחדש את כל הסקריפט בזיכרון.
אתגרונים אתגרונים
ועכשיו לשני אתגרים קטנים שתשתגעו עליהם.
1. איך לגרום לסקריפט החדש שרץ (
2. איך לסגור את הסקריפט הפשוט הבא (הקובץ המצוטט בערוץ) כשאתם מריצים אותו ברקע כך
#challenge
#arguments
#bash_pid
@bash_tips
רמת קושי: #advanced
בעבר דברנו על כך שכשמעבירים לסקריפט באש פרמטרים אז הם נכנסים לסקריפט כמשתנים בשמות $1 לפרמטר הראשון ו$2 $3 לשני והשלישי בהתאמה וכו', מצליחים לנחש איזה ערך נמצא במשתנה $0?
$ cat test.sh
echo "$0"
$ ./test.sh
./test.sh
כן!, הפרמטר הראשון של כל סקריפט הוא השם של הסקריפט עצמו.עכשיו המח הקודח שלנו מיד נכנס לפעולה וחושב אה מגניב אז ככה בעצם אפשר ליצור סקריפט שקורא לעצמו. רקורסיה!!!!1 💪💪💪
$ cat test.sh
echo "$0 | $$"
sleep 20
$0
$ ./test.sh
./test.sh | 101394
./test.sh | 101395
./test.sh | 101396
...
מה היה לנו כאן בעצם?$0
מעביר את שם הסקריפט$$
מציג את מספר התהליך של הסקריפטהסקריפט נח ל20 שניות ומריץ את עצמו שוב
כמו שאתם מבינים החולשות של רקורסיה נמצאות גם כאן, כל התהליכים נוצרים תחת התהליך המקורי, הסקריפט אף פעם לא מגיע לתהליך סיום ולכן כל הרצה שומרת מחדש את כל הסקריפט בזיכרון.
אתגרונים אתגרונים
ועכשיו לשני אתגרים קטנים שתשתגעו עליהם.
1. איך לגרום לסקריפט החדש שרץ (
$0
) להחליף את מספר התהליך שלו מבלי להיות תת תהליך של התהליך המקורי ובכך לגרום לסקריפט המקורי להגיע לנקודת סיום ולשחרר את הזיכרון.2. איך לסגור את הסקריפט הפשוט הבא (הקובץ המצוטט בערוץ) כשאתם מריצים אותו ברקע כך
$ ./bash_tips-stop_recursion.sh &
תוכן הקובץ$ batcat bash_tips-stop_recursion.sh
───────┬────────────
│ File: test.sh
───────┼────────────
1 │ #!/bin/bash
2 │
3 │ echo "$0 | $$"
4 │ sleep 1
5 │ $0
מוזמנים לשתף בתגובות מה דגתם#challenge
#arguments
#bash_pid
@bash_tips
טיפ נהדר של @dank3y והזוית של באש
רמת קושי: #advanced
משתמשי Zsh
אם אתם על הטרמינל כותבים פקודה כלשהי, מעבר לשימוש ב-vi mode של zsh כדי לערוך אותה,
לחיצה על ctrl + e (או מקש אחר לבחירתכם) תביא אתכם ל- vim על מלא בו תוכלו לערוך את הקוד ממש כאילו היה מיני סקריפט שלכם.
כשתסיימו תוכלו לשגר אותו ל-prompt שלכם עם שמירה ויציאה כמו שאתם מכירים.
נדרש רק להוסיף את שתי השורות האלו לקובץ
ניתן להשתמש בפונקציונליות דומה אין צורך להגדיר שום דבר, קיצור המקשים הוא
בשני המקרים העורך שיפתח הוא העורך שמוגדר למשתנה הסביבה
רמת קושי: #advanced
משתמשי Zsh
אם אתם על הטרמינל כותבים פקודה כלשהי, מעבר לשימוש ב-vi mode של zsh כדי לערוך אותה,
לחיצה על ctrl + e (או מקש אחר לבחירתכם) תביא אתכם ל- vim על מלא בו תוכלו לערוך את הקוד ממש כאילו היה מיני סקריפט שלכם.
כשתסיימו תוכלו לשגר אותו ל-prompt שלכם עם שמירה ויציאה כמו שאתם מכירים.
נדרש רק להוסיף את שתי השורות האלו לקובץ
.zshrc
שלכם:autoload edit-command-line; zle -N edit-command-line
bindkey '^e' edit-command-line
משתמשי Bashניתן להשתמש בפונקציונליות דומה אין צורך להגדיר שום דבר, קיצור המקשים הוא
Ctrl + X
Ctrl + E
בשני המקרים העורך שיפתח הוא העורך שמוגדר למשתנה הסביבה
$EDITOR
#editor
@bash_tipsחיפוש
לבצע חיפוש על טקסט ללא רגישות ל case sensitive
ניתן לחפש ביטוי על מספר רב של קבצים, גם כאלו במבנה היררכי
כל שורה תכיל את הנתיב לקובץ שהכיל את ההתאמה ואת ההתאמה
grep
רמת קושי: #advanced
פקודת grep מלאה בפיצ'רים, בכל אופן אני מוצא את עצמי משתמש בכמה עיקריים בתדירות יחסית גבוהה, בדרך כלל כמה מהם ביחדלבצע חיפוש על טקסט ללא רגישות ל case sensitive
$ grep -i "name" file
name: test
Last Name
שימוש בדגלי A / B / C יאפשר לנו להגדיר שלא רק השורה שתואמת את הביטוי תוצג, אלא גם אלו שסביבה, A יחזיר את השורות הבאות לאחר השורה שנמצאה בה התאמה לביטוי, B יחזיר את השורות שקדמו לשורה התואמת לביטוי, C יציג בשווה שורות לפני ואחרי $ grep -A2 "three" /tmp/test
three
four
five
$ grep -B2 "three" /tmp/test
One
two
three
$ grep -C2 "three" /tmp/test
One
two
three
four
five
הוספת מספרי שורות לתוצאות בהם נמצאה ההתאמה$ grep -n "three" /tmp/test
3:three
היפוך תוצאות החיפוש, להחזיר רק את מה שאינו תואם לביטוי$ grep -v "three" /tmp/test
One
two
four
five
בכדי לקבל פונקציולניות מתקדמת ל regex כמו lookbehind, ניתן להשתמש במנוע הregex שתואם את perl, מוכר גם בשם PCRE$ grep -P
ברירת מחדל grep יחזיר את כל השורה בה נמצא מילה התואמת לביטוי, בכדי לשלוף רק את הרצף שתואם לביטוי ניתן להשתמש בדגל -o
$ grep name Chart.yaml
name: Test
grep -o name Chart.yaml
name
ניתן לחפש ביטוי על מספר רב של קבצים, גם כאלו במבנה היררכי
כל שורה תכיל את הנתיב לקובץ שהכיל את ההתאמה ואת ההתאמה
$ grep -r "NodePort" sonarqube
sonarqube/k8s/sonarqube-service.yml: type: NodePort
test_env/k8s/backend-service.yml: type: NodePort
בכדי לקבל רק את הנתיב לקובץ שתואם לביטוי, ניתן להשתמש בנוסף בדגל -l
$ grep -rl "NodePort" sonarqube
sonarqube/k8s/sonarqube-service.yml
test_env/k8s/backend-service.yml
וכמובן שאפשר לקבל רק את הקבצים שאינם מכילים את הביטוי על ידי דגל -L
$ grep -rL "NodePort" sonarqube
וכן אתם לא טועים אפשר לממש את אותו הרעיון עם דגל v$ grep -rlv "NodePort" sonarqube
#grep
@bash_tips
Bash Tips
Photo
באשיזם? shellcheck
רמת קושי: #advanced
באשיזם שמעתי מישהו קורא לזה, זאת בערך המקבילה של באש לפייתוניק קוד, הרעיון הוא לכתוב קוד קריא ויפה שעובד עם השפה. בהקשר הזה כדאי להכיר לינטר לshell (באש זיש דש וכו') המוכר בעולם בשם shellcheck.
מדובר על כלי שיציג הערות על הקוד לפני שאנו פוגשים אותם בטרמינל כשגיאות, shellcheck יחסית מוכר ויש לו פלאגינים כמעט לכל IDE / Editor שקיים (בתמונה), במידה ומשתמשים בIDE תוכלו לראות את השגיאות כבר בעורך, מי שמתעקש להריץ בדיקות מהטרמינל (CI?) זה נראה כך
רמת קושי: #advanced
באשיזם שמעתי מישהו קורא לזה, זאת בערך המקבילה של באש לפייתוניק קוד, הרעיון הוא לכתוב קוד קריא ויפה שעובד עם השפה. בהקשר הזה כדאי להכיר לינטר לshell (באש זיש דש וכו') המוכר בעולם בשם shellcheck.
מדובר על כלי שיציג הערות על הקוד לפני שאנו פוגשים אותם בטרמינל כשגיאות, shellcheck יחסית מוכר ויש לו פלאגינים כמעט לכל IDE / Editor שקיים (בתמונה), במידה ומשתמשים בIDE תוכלו לראות את השגיאות כבר בעורך, מי שמתעקש להריץ בדיקות מהטרמינל (CI?) זה נראה כך
$ shellcheck deploy.sh
In deploy.sh line 3:
eval $(aws ecr get-login --region ap-southeast-2 --no-include-email)
^-- SC2046: Quote this to prevent word splitting.
For more information:
https://www.shellcheck.net/wiki/SC2046 -- Quote this to prevent word splitt...
במידה ואנו רוצים להחריג התנהגות מסוימת במקום ספציפי בקוד אף על פי שהיא אינה קבילה מבחינת shellcheck, ניתן להוסיף הערת disable עם מספר השגיאה מעל השורה הבעייתית# shellcheck disable=SC2046
eval $(aws ecr get-login --region ap-southeast-2 --no-include-email)
במידה ורוצים להתעלם מאותה שגיאה לכל אורך הפרויקט ניתן להריץ את shellcheck בצורה הבאה$ shellcheck -e SC2046 deploy.sh
#shellcheck
@bash_tipsהעברת מידע בין תהליכים ()>
רמת קושי: #advanced
הטיפ הבא מניח ידע בהפניות וערוצים
שימוש ב pipe
צורת העבודה עם באש מורכבת מאוסף של כלים קטנים שמשרשרים אותם ביחד כדי לקבל תוצאה מרשימה שיותר, לשם כך הכלי העיקרי שלנו הוא שימוש ב pipe (
שימוש ב Process Substitution
קיימת אפשרות נוספת להעביר פלט של פקודה אחת לפקודה אחרת והוא על ידי שימוש בהחלפת הקונטקסט לתהליך.
היכולת הזאת מאפשרת לנו לבצע
העברת פלט של פקודות לפקודות אחרות שעובדות רק עם קבצים
ועוד יכולת קריטית נוספת שנראה בטיפ הבא
#pipe
#redirection
#named_pipe
#process_substitution
@bash_tips
רמת קושי: #advanced
הטיפ הבא מניח ידע בהפניות וערוצים
שימוש ב pipe
צורת העבודה עם באש מורכבת מאוסף של כלים קטנים שמשרשרים אותם ביחד כדי לקבל תוצאה מרשימה שיותר, לשם כך הכלי העיקרי שלנו הוא שימוש ב pipe (
|
)$ echo "(5 + 5) * 1000" | bc
10000
מה שבעצם מתרחש כאן הוא היכולת של באש להעביר פלט של פקודה אחת לפקודה אחרת. אם נרצה להסביר את הפעולה הזאת בשפה היותר טכנית, באש מפנה את ה stdout של הפקודה הראשונה ל stdin של הפקודה שמגיע לאחר הpipe.שימוש ב Process Substitution
קיימת אפשרות נוספת להעביר פלט של פקודה אחת לפקודה אחרת והוא על ידי שימוש בהחלפת הקונטקסט לתהליך.
$ more <(ls /bin)
מה בעצם קורה מתחת למכסה המנוע? התהליך הראשון רץ בסביבה משלו ואת התוכן הוא מעביר לnamed pipe, שזה שקול בערך לכתיבה לקובץ, ולאחר מכן הנתיב "לקובץ" המדובר עובר לפקודה הבאה.$ less <(ls /bin)
/proc/self/fd/11 is not a regular file (use -f to see it)
$ cat <(ls /bin)
[
2to3-2.7
411toppm
7z
...
$ less < <(ls /bin)
<less open with the relevant content>
כפי שניתן לראות ישנן פקודות שיודעות לקרוא תוכן ישירות מ named pipe וישנן כאלה שלא, בכדי "להמיר" את התוכן לפקודות שאינן תומכות בקריאה ישירות מ named pipe ניתן להפנות את הפלט אל ה stdin של הפקודה בעזרת >
(כפי שקורה בדוגמה האחרונה).היכולת הזאת מאפשרת לנו לבצע
העברת פלט של פקודות לפקודות אחרות שעובדות רק עם קבצים
ועוד יכולת קריטית נוספת שנראה בטיפ הבא
#pipe
#redirection
#named_pipe
#process_substitution
@bash_tips
לעבד stderr בלי לאבד את קוד היציאה
רמת קושי: #advanced
הטיפ הבא מניח ידע בהפניות וערוצים
לאחר שראינו שניתן להעביר פלט של פקודות מאחת לשניה כדי לעבד אותן, כדאי שנכיר בעובדה שניתן לעבד אך ורק פלט שמועבר על ידי ה stdout ואילו פלט של stderr יוצג כמות שהוא
בכדי לפתור את הבעיה הזאת אנו יכולים לחבר בין הערוצים ולהפנות את ה stderr אל stdout בצורה הבאה.
אם כך הכל מדהים, היכן הבעיה?
הבעיה עם צורת העבודה הזאת היא שאנו מאבדים את קוד היציאה של התוכנית.
הפתרון Process Substitution
בכדי לפתור את הבעיה הזאת נוכל להשתמש בקונספט שראינו בטיפ הקודם רק בצורה ההפוכה.
$
הטיפ הבא שימושי מאוד כשעובדים עם כלים המאפשרים הרצת פקודות ad hoc כמו דוקר, ורוצים לעבד את המידע מבלי לפגוע בפלט הגולמי
#exit_code
#redirection
#process_substitution
@bash_tips
רמת קושי: #advanced
הטיפ הבא מניח ידע בהפניות וערוצים
לאחר שראינו שניתן להעביר פלט של פקודות מאחת לשניה כדי לעבד אותן, כדאי שנכיר בעובדה שניתן לעבד אך ורק פלט שמועבר על ידי ה stdout ואילו פלט של stderr יוצג כמות שהוא
$ ls /not_exist_path | grep -Po "cannot access"
ls: cannot access '/not_exist_path': No such file or directory
הסיבה לכך היא שpipe מעביר אך ורק את ה stdout ולא את ה stderr.בכדי לפתור את הבעיה הזאת אנו יכולים לחבר בין הערוצים ולהפנות את ה stderr אל stdout בצורה הבאה.
$ ls /not_exist_path 2>&1 | grep -Po "cannot access"
cannot access
אם כך הכל מדהים, היכן הבעיה?
הבעיה עם צורת העבודה הזאת היא שאנו מאבדים את קוד היציאה של התוכנית.
$ ls /not_exist_path 2>&1 | grep -Po "cannot access"
cannot access
$ echo $?
0
ברגע שהפקודה הראשונה נכשלת היא לא יוצאת משרשור הפקודות שהעברנו לה אלא מעבירה את הפלט לפקודה הבאה שהיא בתורה מבצעת את התפקיד שלה נהדר ולכן הסטטוס שיחזור יהיה 0 כפי שמחזירה הפקודה האחרונה ואנו נאבד את קוד השגיאה של הפקודה הראשונה.הפתרון Process Substitution
בכדי לפתור את הבעיה הזאת נוכל להשתמש בקונספט שראינו בטיפ הקודם רק בצורה ההפוכה.
$
ls /not_exist_path &> >(grep -Po "cannot access")
cannot access
$ echo $?
2
מה שבעצם קורה כאן הוא שאנו מאחדים את שני הערוצים לערוץ אחד אבל מבצעים את פעולת ההמשך שלנו בקונטקסט אחר, בסאב שאלל ולכן קוד היציאה של הפקודה הראשונית נשאר אותו הדבר.הטיפ הבא שימושי מאוד כשעובדים עם כלים המאפשרים הרצת פקודות ad hoc כמו דוקר, ורוצים לעבד את המידע מבלי לפגוע בפלט הגולמי
#exit_code
#redirection
#process_substitution
@bash_tips
מניפולציה על מערכים
רמת קושי: #advanced
דברנו בעבר על כך שבאש מאפשרת לבצע מניפולציה על משתנים, להגדיל אותיות או להקטין, להחליף תוכן בתוכן אחר וכו'
מסתבר שאת כל הפעולות הללו ניתן לבצע גם על מערך שלם, מבלי לרוץ עליו בלולאה.
לדוגמה, להלן מערך של משתמשים שכל איבר בו מכיל שם משתמש וסיסמה, פעם אחת אנו רוצים לקבל את המשתמשים ופעם אחרת רק את הסיסמאות
אם נמשיך עם הרעיון הזה נראה שאפשר לממש מילון עם מפתח מכל סוג שנרצה, על ידי כך שנעביר מחרוזת וכשנרצה לשלוף אותה, נבצע מניפולציה על התוכן
#array
#parameter_expansion
@bash_tips
רמת קושי: #advanced
דברנו בעבר על כך שבאש מאפשרת לבצע מניפולציה על משתנים, להגדיל אותיות או להקטין, להחליף תוכן בתוכן אחר וכו'
$ myvar=my_username:my_password
$ echo ${myvar^^}
MY_USERNAME:MY_PASSWORD
$ echo ${myvar//my/your}
your_username:your_password
מסתבר שאת כל הפעולות הללו ניתן לבצע גם על מערך שלם, מבלי לרוץ עליו בלולאה.
לדוגמה, להלן מערך של משתמשים שכל איבר בו מכיל שם משתמש וסיסמה, פעם אחת אנו רוצים לקבל את המשתמשים ופעם אחרת רק את הסיסמאות
$ users_auth=(admin:1234356)
$ users_auth+=(myuser:456789)
$ users_auth+=(writer:00001)
$ echo ${users_auth[@]}
admin:1234356 myuser:456789 writer:00001
$ echo ${users_auth[@]/:*}
admin myuser writer
$ echo ${users_auth[@]/*:}
1234356 456789 00001
אם נמשיך עם הרעיון הזה נראה שאפשר לממש מילון עם מפתח מכל סוג שנרצה, על ידי כך שנעביר מחרוזת וכשנרצה לשלוף אותה, נבצע מניפולציה על התוכן
#array
#parameter_expansion
@bash_tips
להחריג תווים $
רמת קושי: #advanced
אנו יודעים שבשביל להחריג תווים מיוחדים ממחרוזת בקוד משתמשים בלוכסן, זה כך כמעט בכל השפות ובאש אינה יוצאת דופן.
יוצאת דופן אמרנו? הכוונה שהיא משתדלת לא לצאת דופן, עדין ישנן מקומות בהם לוכסן לא מבצע שום פעולה
בכדי שיהיה אפשרי להשתמש בלוכסן במקרה כגון זה, צריך להריץ את הפקודה עם דולר בראש המחרוזת
מקום נוסף שזה בא ליידי ביטוי
נושא זה נקרא ANSI-C
למי שרוצה להרחיב את הקריאה על כך
#echo
#ansi_c
@bash_tips
רמת קושי: #advanced
אנו יודעים שבשביל להחריג תווים מיוחדים ממחרוזת בקוד משתמשים בלוכסן, זה כך כמעט בכל השפות ובאש אינה יוצאת דופן.
יוצאת דופן אמרנו? הכוונה שהיא משתדלת לא לצאת דופן, עדין ישנן מקומות בהם לוכסן לא מבצע שום פעולה
$ echo "name='version'"
name='version'
$ echo 'name=\'version\' '
> ^C
בכדי שיהיה אפשרי להשתמש בלוכסן במקרה כגון זה, צריך להריץ את הפקודה עם דולר בראש המחרוזת
$ echo $'name=\'version\''
name='version
'מקום נוסף שזה בא ליידי ביטוי
$ echo $'first line \nsecond line'
first line
second line
נושא זה נקרא ANSI-C
למי שרוצה להרחיב את הקריאה על כך
#echo
#ansi_c
@bash_tips