Bash Tips
985 subscribers
14 photos
4 files
45 links
רוצים להשתמש בלינוקס אבל לא ממש מכירים את הכלים שהיא מספקת לעבודה?

בערוץ הבא תמצאו אוסף טיפים שימושיים ב-Bash והכרות עם כלים שונים שעשויים לחסוך מאמץ ועבודה בכתיבת סקריפטים ומימוש אוטומציות.
Download Telegram
פוסט מעט אחר, על אנדרואיד ובאש
רמת קושי: #beginners

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

איפה היינו? 🤔 באש כן
לא זכור לי שצריך להתקין משהו מיוחד בשביל באש, בכל אופן פקודת הקסם שלנו בשביל להוסיף איזו חבילה שהיא למערכת היא על ידי

$ pkg install python

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

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

הגדרת termux
ברוח השימוש בטרמינל כל הגדרות שנרצה להגדיר לאפליקציה יעשו דרך הקובץ הבא שיהיה "בתיקיית הבית"

~/.termux/termux.properties

אני מגרגש את המונח תיקיית הבית כי תכלס מדובר על תיקייה שנמצאת בנתיב הבא והוא מוגדר ביחס ל termux ולא ביחס לתיקיית הבית של המכשיר

/data/data/com.termux/files/home

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

אחסון
ברירת מחדל "לתיקיית הבית" יש הרשאות רק לroot ולבעלים של התיקייה, מאחר ומשתשמש האנדרואידי שלנו לא נכלל ברשימה הקצרה הזאת, נצטרך למצוא דרך להעביר קבצים שאנו מורידים אל המכשיר עצמו.

לדרך הזאת יש שם או יותר נכון פקודה שמבקשת מהמשתמש לאשר גישה לאחסון כך שיהיה ניתן לשמור אליו דברים ישירות, הרצה של הפקודה הבאה תיצור תיקיית storage בה יש לינקים סימבולים למספר תיקיות במכשיר

$ termux-setup-storage

חשוב לזכור שזאת מערכת לינוקס ותמיד אפשר לשנות הרשאות או ליצור לינקים סימבוליים נוספים לרחבי המערכת

התממשקות לAPI של המכשיר
מסתבר שהחברה ב Termux הגדילו לעשות, ישנה התממשקות לapi של אנדרואיד וניתן לתשאל את המערכת בכל מיני צורות, זה דיי מוגבל ומגרסת פאי זה מוגבל אפילו יותר, בכל אופן לאלו שרוצים להשתעשע מוזמנים להתקין את החבילה הבאה

$ pkg install termux-api

התיעוד לא ממש מעודכן, בכל אופן הוא נמצא כאן

לסיום כדאי לעבור על התיעוד, מן הסתם לא הקפתי את כל הדברים ש Termux מאפשרת אלא רק את מה שנגע בקצה המזלג שלי, הלינק המעניין הוא כאן

#termux
#android

@bash_tips
על אנדרואיד ובאש פרק ב
רמת קושי: #beginners

אחת הסיבות שאני אוהב לשתף מה למדתי היא השיח המעשיר שמגיע לאחר מכן, בקצרה אלו הדברים שלמדתי בעקבות הפוסט שכתבתי על Termux, ותודה רבה לכל נותני ההערות והמעשירים למינם, תבואו לעיתים קרובות יותר 😃

התקנת Tremux
את Termux לא כדאי להתקין מחנות האפליקציות של גוגל אלא מ Fdroid, הסיבה לכך היא שהמפתחים הפסיקו לעדכן את האפליקציה בגוגל פליי (גרסת SDK ישנה), במקום זאת העדכונים עולים לחנות המקבילה והחופשית.

למי שלא מכיר F-droid היא חנות אפליקציות המקבילה לגוגל פליי, מוכרת מאוד בעולם הקוד פתוח אבל לא רק, שמה הוא בעצם קיצור של FOSS Droid.

תוספי Termux בחינם
לאחר שהתקנתי את החבילה מ Fdroid ראיתי שגם כל התוספים הנלווים ל Termux מוצעים בחינם להורדה, מדובר על תוספים שבתשלום בחנות של גוגל מן הסתם זה עדין כך עקב חוסר העדכון, תוסף שאהבתי הוא Widget המחזיק רשימת סקריפטים ומאפשר להריץ אותם ישירות ממסך הבית.
כמובן יש עוד, זאת הרשימה.

התממשקות לAPI של המכשיר
בניגוד למה שכתבתי בפוסט הקודם נראה שהתמיכה ב API של אנדרואיד אפילו גדלה, רשימה נאה של פונקציונליות ניתן לראות כאן,

באג בהשלמה אוטומטית
למשתמשי מקלדת samsung יש בעיית התממשקות לTermux, ההשלמה האוטומטית משלימה דיי מה שמתחשק לה, הבעיה ידועה וניתנת לפתרון על ידי הורדת ההערה מהשורה הבאה בקובץ ההגדרות של Termux הלא הוא .termux/termux.properties

enforce-char-based-input = true

טעינה מחדש של ההגדרות על ידי termux-reload-settings, והסיפור סגור

כמו תמיד מוזמנים לשתף ולהגיב

#termux
#android

@bash_tips
לוג לפונקציות, צנזור מידע ו subshell
רמת קושי: #advanced

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

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

get_page(){
logger "run function: ${FUNCNAME[0]} with parameters: ${*}"
echo "${page}"
}

וכך נראית הסקריפט כשאנו מריצים אותו

$ bash test.sh https://google.com
[*] run function: get_page with parameters: https://google.com

ולמי שמאוד סקרן כך נראית פונקציית logger

logger() {
status=$?
if [[ $status -eq 0 ]]; then
echo -e "\e[92m[*] ${@}"
else
echo -e "\e[91m[!] ${@}"
fi
}

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

$ test.sh https://admin.site myuser mypass
[*] run function: get_page with parameters: https://admin.site myuser mypass

מה בעצם אנו צריכים?
אנחנו רוצים בעצם יכולת לצנזר מידע רגיש שעובר ללוגר, העניין הוא שלא תמיד נדע איזה מידע אנו רוצים לצנזר ואם בכלל צריך לצנזר, לכן אנו צריכים אופציה להגדיר מהו מידע רגיש מחוץ ללוגר ושנוכל להשתמש באופציה הזאת רק מתי שנחוץ לנו.

אפשרות למימוש
פשוט להריץ פקודת sed שתעשה החלפה למחרוזות שהלוגר מקבל, על ידי הגדרה של שני משתנים כשהראשון מקבל regex מה להחליף והשני מגדיר לאיזה מחרוזת להחליף.
נשנה מעט את הפונקציה שלנו וכעת היא נראית כך.

get_page(){
input=$(sed "s|${REPLACE}|${REPLACE_TO}|g" <<<"${@}")
logger "run function: ${FUNCNAME[0]} with parameters: ${input}"
echo "${page}"
}

וכך אנו קוראים לפונקציה בסקריפט

REPLACE="myp.*"
REPLACE_TO="***"
get_page "${@}"

נבדוק רגע נראה שהכל עובד תקין

$ test.sh https://admin.site myuser mypass
[*] run function: get_page with parameters: https://admin.site myuser ***

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

REPLACE="myp.*"
REPLACE_TO="***"
get_page "${@}"
unset REPLACE
unset REPLACE_TO

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

(
REPLACE="myp.*"
REPLACE_TO="***"
page=$(get_page "${@}")
)

#scripting
#subshell
#function

@bash_tips
המדריך_לטרמינל_לינוקס_למתחילים_Qazjap11.pdf
236.2 KB
רמת קושי: #beginners

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

@bash_tips
יציאה מלולאה מקוננת
רמת קושי: #beginners

בבאש כמו בכל שפת תכנות בשביל להפסיק ריצה של לולאה משתמשים בב break,

for i in {1..10}
do
echo $i
sleep 1
  break
done

מה עושים כשהלולאה רצה בתוך לולאה אחרת (לולאה מקוננת) ורוצים לצאת משתי הלולאות?

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

for i in {1..10}
do
for j in {1..10}
do
echo $i
sleep 1
break 2
done
echo first loop
done

#loops

@bash_tips
אוהבים אתגרים? הנה אתר גאוני ממש
רמת קושי: #advanced

https://oops.cmdchallenge.com/
https://12days.cmdchallenge.com/


אתם רק מתחילים ורוצים להכיר את באש בצורה חווייתית יותר?
רמת קושי: #beginners

https://cmdchallenge.com/


#challenge
#tools

@bash_tips
קוד קריא pipe
רמת קושי: #beginners

מכירים את זה ששרשרתם כמה פקודות עם pipe כדי לפלטר בדיוק את המידע שרציתם מהפלט אבל קיבלתם שורה ארוכה ולא קריאה בעליל?

$ curl https://google.com -v --trace-time 2>&1 | grep "^[0-9]" | cut -d ' ' -f1 | cut -d ':' -f3

אז מסתבר שאפשר לפצל שורות בעזרת ה pipe, קוד הגיוני סך הכל

$ curl https://google.com -v --trace-time 2>&1 |
grep "^[0-9]" |
cut -d ' ' -f1 |
cut -d ':' -f3


#pipe

@bash_tips
להמיר מחרוזת למערך
רמת קושי: #beginners

כפי שאנו מכירים יצירת מערך בבאש נעשית על ידי הכנסת ערכים לסוגריים עגולים

$ arr=(1 2 3)
$ declare -p arr
declare -a arr=([0]="1" [1]="2" [2]="3")

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

$ numbers="1 2 3"
$ myarr=($(echo "${numbers}"))

האמת זה עובד נפלא, shellcheck לא ממש אוהב את זה, אבל זהו קוד שעובד.

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

קריאה למשתנה "מגורגש"

$ numbers="1 2 3"
$ myarr=("${numbers}")
$ declare -p myarr
declare -a myarr=([0]="1 2 3")

קריאה למשתנה נטול גרשיים

$ myarr=(${numbers})
$ declare -p myarr
declare -a myarr=([0]="1" [1]="2" [2]="3")

היכן הפינות?
במידה והמחרוזת שלנו מכילה ערכים שהם globbing characters מה שיקרה הוא שבאש יתייחס לתווים הללו כבעלי משמעות ולא יתייחס אליהם כתווים רגילים.

$ ls
1.txt 2.txt 3.txt

$ myvar="text with wildcard *"
$ mylist=( $(echo "$myvar") )
$ declare -p mylist
declare -a mylist=([0]="text" [1]="with" [2]="wildcard" [3]="1.txt" [4]="2.txt" [5]="3.txt")

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


#globbing_characters
#declare
#array

@bash_tips
תוסף bash-ide
רמת קושי: #beginners

מי שמשתמש ב VScode ודאי שם לב שאין שום תמיכה של סביבת עבודה בכל מה שקשור לבאש, לשם כך יש את התוסף הבא, וכמו בכל תוסף שווה להסתכל בהגדרות.

https://marketplace.visualstudio.com/items?itemName=mads-hartmann.bash-ide-vscode

#tools

@bash_tips
Bash Tips
רצפים ופורמטים עם seq רמת קושי: #beginners כפי שאנו מכירים באש מאפשרת ליצור רצפים של מספרים על ידי שימוש ב Brace expansion (הביטוי שמתאר את הפעולה הבאה {10..1} ) וזה עובד יפה מאוד כשמשתמשים בזה עם מספרים cat myscript.sh for i in {1..10}; do echo $i…
איחוד רצפים seq
רמת קושי: #beginners

פקודת seq עליה דיברנו בעבר מאפשרת ליצור רצף של תווים
תכונה שימושית נוספת שהפקודה מכילה היא האפשרות לקבוע איזה תו יפריד בין כל תו/מילה/מספר שהיא יוצרת

דוגמה שימושית בתכונה זו היא יצירת מחרוזת לregex שבוחרת שנים בין 2016 ל 2021 מבלי להיכנס לסינטקס הלא ברור של regex

$ seq -s "|" 2016 2021
2016|2017|2018|2019|2020|2021



#seq
#regex

@bash_tips
השמה למשתנה בתהליך תנאי
רמת קושי: #beginners

באש מאפשרת ליצור תנאים שמבוססים על הרצה של פקודה, התנאי הבא שווה לtrue במידה והפקודה הצליחה ולא נכשלה.

if curl "${my_url}" ; then
my_logger "link to song: ${my_url}"
fi


ובשביל תנאי הפוך שמממש את ה-if מתי שהפקודה נכשלת מוסיפים ! לפני הפקודה

if ! curl "${my_url}" ; then
my_logger "failed to fetch: ${my_url}"
fi


עד כאן כולם מכירים
מה שפחות מכירים היא האפשרות של השמה למשתנה כחלק מתהליך התנאי.

if output=$(curl "${my_url}" | grep "rozy-*"); then
my_logger "user ${output} looged in"
fi


כפי שניתן לראות אנו מבצעים השמה למשתנה כחלק מתהליך התנאי, במידה והפקודה נכשלת המשתנה נשאר ריק ולא תתבצע שום השמה


#if
#conditions
#assignment

@bash_tips
לעבוד עם טווח, cut
רמת קושי: #beginners

פקודת cut מאפשרת לבחור תווים או עמודות מהטקסט על ידי אינדקס או טווח

$ echo ABCDEFG | cut -c 3
C

$ echo ABCDEFG | cut -c 3-5
CDE

$ echo ABCDEFG | cut -c 1,3,5
ACE

$ echo ABCDEFG | cut -c 3-
CDEFG

מה היה לנו כאן? דגל c- (characters) מקבל תו/מערך/טווח מתוך טקסט ומחזיר אותו, בדוגמה האחרונה אפשר לראות איך מגדירים את הטווח עד סוף הטקסט מבלי לתת אינדקס ספציפי, צורה העבודה של תווים עובדת כמעט אחד לאחד עם עמודות, מה שמוביל לשאלה הבאה

מהם עמודות אם כך?
זהו בעצם החלק השימושי באמת בפקודה, עמודות הן כל קטע טקסט שנחליט, ברגע שנגדיר עם d (delimiter)- שתו מסויים הוא זה שמפצל את הטקסט, כל הטקסט הופך להיות כעין טבלה דמיונית שאפשר לבחור ממנה מידע על פי מיקום העמודה, וטוב מראה עיניים אז...

$ cat users
name,user id,telegram account
coco,5635634356,no
roni,996873823,yes
tom,7424743356,yes

$ cut -f 1 -d , users
name
coco
roni
tom

$ cut -f 2 -d , users
user id
5635634356
996873823
7424743356

יש לנו קובץ בשם users שמכיל מידע בתצורת CSV (פסיקים שמפרידים בין ערכים), הגדרנו חלוקה לפי תו פסיק ומשם ניתן לבחור איזה עמודה להציג עם ידי העברת אינדקס העמודה לדגל -f

דגל -d מקבל תו אחד בלבד, כשרוצים לפצל לפי רווח ניתן לגרגש את הרווח, הטריק הזה לצערנו לא יעבוד עם תווי שורה חדשה, טאב וכו'

$ echo "Good Morning" | cut -f 1 -d ' '
Good

$ echo "Good\nMorning" | cut -f 1 -d '\n'
cut: the delimiter must be a single character
Try 'cut --help' for more information.

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

cut -f 1,3,5 -d ' '

רצף

cut -f 3-5 -d ' '


או מעמודה מסויימת עד העמודה האחרונה

cut -f 3- -d ' '


#cut

@bash_tips
לפרמט את היסטוריה
רמת קושי: #beginners

כלי מוכר בלינוקס מאפשר לראות את ההיסטוריה של הפקודות שהורצו על מכונה או משתמש מסויים על ידי הרצה של פקודת history

למה לפרמט?

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

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

$ export HISTTIMEFORMAT="%d/%m/%y %T "
$ history
256 11/06/21 11:05:32 init 0
257 11/06/21 11:05:32 su user -
258 11/06/21 11:05:32 ls
259 11/06/21 11:05:32 python
260 11/06/21 11:05:32 rm users
...


הערות
* לכל הדואגים עדין אפשר להריץ !258.
* כמו שאנו מכירים בכדי לגרום להתנהגות הזאת להישאר קבועה, פשוט להכניס את הexport לbashrc ולעשות source.


#history
#env_variable

@bash_tips
שימוש חוזר בהיסטוריה
רמת קושי: #beginners

אם כבר בהיסטוריה עסקינן אז סימן קריאה ומספר השורה יריץ את שורה המצויינת בהיסטוריה שוב

$ history
100 ls
101 ls -lah
102 tree
...

$ !102
.
├── [
├── a2p
├── addr2line
├── alias


בכדי להריץ את הפקודה האחרונה שכתבתם שוב או כדי להעביר אותה כפרמטר ניתן להשתמש ב!!

abc@abc-home:~$ apt install aaa
E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)
E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?

abc@abc-home:~$ sudo !!
sudo apt install aaa
[sudo] password for
abc:


#history
#operations

@bash_tips
לוגים, הקדמה 1 tee
רמת קושי: #beginners

פקודת tee עושה דבר מאוד פשוט ומאוד שימושי, הפקודה מפצלת פלט כך שיכתב גם למסך וגם לקובץ.

$ echo 123 | tee myfile
123
$ cat myfile
123


נראה עוד דוגמה

$ ls /not_exist | tee myfile
ls: cannot access /not_exist: No such file or directory
$ cat myfile
$


רגע מה קורה למה הקובץ לא מכיל את הודעת השגיאה שפקודת ls החזירה?

הסיבה היא ש pipe מעביר רק stdout ולכן כדי להשתמש ב tee בצורה יעילה כדאי לשרשר את stderr לstdout על ידי 2<&1
אם נחזור לדוגמה הקודמת אז הפקודה צריכה להראות כך

$ ls /not_exist 2>&1 | tee myfile
ls: cannot access /not_exist: No such file or directory
$ cat myfile
ls: cannot access /not_exist: No such file or directory

חשוב לציין שבכל הרצה הפקודה תדרוס את הקובץ מחדש, בכדי להוסיף תוכן לקובץ מבלי לדרוס אותו כל פעם מחדש ניתן להשתמש בדגל -a


#tee
#redirection

@bash_tips
לוגים, הקדמה 2 exec
רמת קושי: #advanced

פקודת exec כמו שהיא נשמעת היא פקודת באש שמריצה פקודות באש מתוך באש

bash >> command
bash >> exec >> command


רגע מה???, למה שמישהו ירצה לעזאזל להריץ פקודת באש על פקודת exec במקום להריץ ישירות על באש את הפקודה הרצויה?

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

פקודת exec עובדת בשני צורות אחת עם פרמטר והשניה בלי פרמטר,
הרצת exec עם פרמטר (פקודה) תגרום לכך שרק הפקודה שהועברה כפרמטר תרוץ תחת exec.

$ exec ls > myfile.log

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

$ ls
1.txt 2.txt 3.txt
$ exec > myfile.log
$ ls
$ cat myfile.log
$ 1.txt 2.txt 3.txt


כפי שניתן לראות לאחר שהרצנו exec פקודת ls כבר לא מחזירה פלט לטרמינל אלא ישירות לקובץ.


נ.ב. שימוש בexec עם פרמטר יוצר בעצם את הרצף הבא

bash >> exec >> ls >> exit

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


#exec
#redirection

@bash_tips
לוגים, על exec וניתוב פלט
רמת קושי: #advanced

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


המשימה: יצירת לוג אינפורמטיבי

פתרון 1
הפתרון הראשון שמיד יעלה לנו לראש הוא ליצור איזה פונקציה שעושה echo לכל מה שנעביר לה לתוך קובץ, משהו שנראה כך

logger() {
echo "${@}" | tee myscript.log
}

מה שיש לנו פה זאת פונקצייה פשוטה בשם logger שמדפיסה כל מה שהיא מקבלת לקובץ myscript.log


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

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

אם כך נשנה מעט את המשימה שלנו
המשימה: יצירת לוג אינפורמטיבי אוטומטי

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

$ myscript.sh 2>&1 | tee myscript.log

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


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

exec &> >(tee myscript.log)

מה שיש לנו כאן זוהי בעצם הכרזה בסקריפט שכל פלט שמגיע מהפקודות לאחר הכרזה זו (stderr & stdout) יופנה אל פקודת tee על ידי שימוש ב process_substitution שהיא בתורה תכתוב את התוכן גם לקובץ וגם למסך, וכמו בעבר בכדי להימנע מכלים שיש להם מידע שאינו רלוונטי ללוגים תמיד נוכל להשתיק פלט על ידי הפניה שלו אל /dev/null


אתגר בקצה
אחלה אם כן יש לנו פתרון נהדר, היכן עוד נוכל לשפר אותו?
כמו שאנו מכירים כל לוג אמור להכיל עוד פרטים מעבר לפלט, שעה ותאריך לדוגמה, או כל דבר אחר שנרצה שהלוג יכיל, מן הסתם נרצה שלפני שהפלט יכתב לקובץ הלוג שהוא יעבור תהליך כלשהו כדי שהלוג יהיה קריא ומועיל וכו'

איך נוכל לגרום לפלט להיות בפורמט של לוג?
מוזמנים לשלוח רעיונות


#process_substitution
#logging
#exec
#tee

@bash_tips
עוד קצת tee בבקשה
רמת קושי: #beginners

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


להעביר פלט אחד למספר פקודות על ידי שימוש ב process substitution, לא מדובר כאן על עיבוד פלט ושרשור הלאה, אלה על העברה של פלט גולמי למספר תהליכים שונים (הנה קמח, אני רוצה פיצה ובורקס)

$ echo "date" | tee >(ssh host1) >(ssh hos
t2)


ליצור מספר קבצים בעלי תוכן מסויים, כמובן שניתן רק להוסיף את התוכן על ידי שימוש בדגל -a

$ date | tee {1..10}.
txt


כתיבת מידע לקובץ בעל הרשאות רוט
(תודה ל @tomer על הטיפ!)

$ sudo echo aaa > /root/test
bash: /root/test: Permission denied

$ echo aaa | sudo tee /root/test
aaa
$ sudo cat /root/test
a
aa


#tee
#process_substitution

@bash_tips
מילוי קלט אינטרקטיבי
רמת קושי: #beginners

ישנן 2 דרכים עיקריות להעביר פרמטרים לסקריפט, האחת על ידי העברת הפרמטרים בעת קריאה לסקריפט

$ myscript.sh username passwor
d

וישנה הדרך האנטרקטיבית בה הסקריפט רץ ובשלב מסויים ממתין לקלט מהמשתמש

$ ./myscript.sh
Welcome to my awesome script
Enter your username:


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

בכדי להתגבר על בעיה זו ניתן ליצור קובץ שמכיל את כל הנתונים שהסקריפט הולך לבקש ולשרשר אותם לסקריפט בעזרת pipe, בא נראה איך הקוד של הסקריפט בעצם נראה

$ cat my_interactive_script.py
#!/usr/bin/python3

user = input('Enter username: ')
pwd = input('Enter password: ')

print(f'\nusername: {user}, password: {pwd}')

סקריפט פשוט שמצפה ל2 קלטים מהמשתמש.
כך נראה הקובץ שמכיל את הפרטים אותם אנו רוצים להעביר לסקריפט

$ cat account_details
myusername
mypassword

כעת נשרשר את קובץ נושא הפרטים לסקריפט בצורה הבאה

$ cat account_details | my_interactive_script.py
Enter username: Enter password:
username: myusername, password: mypa
ssword

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

אז מה יצא לנו
העברת נתונים לסקריפט בצורה אוטומטית? יש
מיסוך המידע שהעברנו לסקריפט? יש

#parameters
#interactive

@bash_tips
לרוץ כ root
רמת קושי: #beginners

טיפ חביב שלמדתי, הפקודה שלנו בלינוקס כדי להחליף משתמש היא su, אם כן כשאנו רוצים לשנות לroot מה שנרצה להריץ זה

$ su root 
Password:
su: Authentication failure

הפתעה, מסתבר שזה עובד על כל משתמש אחר אבל על root זה לא עובד
ההתנהגות שראינו ככל הנראה אינה נכונה לכל המערכות אלא להפצות מבוססות אובונטו, הסיבה לכך היא שבאותן הפצות root מגיע ללא סיסמה מוגדרת ולכן אי אפשר לעשות אליו לוגין אלא אם כן משתמשים ב sudo, אם כן

user@home  ~ $ sudo su root
root@home ~

וזה אכן יעבוד לאחר אימות של sudo
דרך קצרה יותר לעשות את זה היא

user@home  ~ $ sudo bash 
root@home ~ $


#bash
#sudo
#su

@bash_tips