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

בערוץ הבא תמצאו אוסף טיפים שימושיים ב-Bash והכרות עם כלים שונים שעשויים לחסוך מאמץ ועבודה בכתיבת סקריפטים ומימוש אוטומציות.
Download Telegram
אוהבים אתגרים? הנה אתר גאוני ממש
רמת קושי: #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
Forwarded from shahar
למצוא את הדרך
רמת קושי: #beginners

כאשר אנחנו מריצים פקודה כגון

$ curl example.com

כיצד המערכת יודעת מה זה curl, ואיפה הבינארי של curl נמצא?

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

/usr/bin/curl example.com

הדרך שבה PATH עובד הוא שאם מוגדר לנו

PATH="/foo:/bar"

ואנחנו מנסים להריץ משהו שנקרא curl, אז המערכת תחפש את הבינארי curl קודם בfoo ואחר"כ בbar, במידה והיא מצאה בfoo את הבינארי, המערכת תריץ את curl שבתוך foo, ואפילו לא תחפש את curl בתוך bar.

במידה ולא נמצא שום בינארי בתוך התקיות המצויינות בPATH המערכת תזרוק שגיאה שתגיד:

curl: command not found

אפשרי להוסיף תקיות משלנו ע"י לשים שורה כזאת בתוך .bashrc

export PATH="/path/to/my/scripts/dir:$PATH"

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


#env_variable
#path

@bash_tips
shahar
למצוא את הדרך רמת קושי: #beginners כאשר אנחנו מריצים פקודה כגון $ curl example.com כיצד המערכת יודעת מה זה curl, ואיפה הבינארי של curl נמצא? משתנה הסביבה PATH הוא זה שנותן לנו להריץ פקודות ללא צורך בציון הנתיב המלא לבינארי שאנו מנסים להריץ, הוא הסיבה שאנחנו…
זה המקום להגיד תודה רבה לשחר @bananabun על הפוסט ועל אלו שעוד יגיעו אחריו.

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

@bash_tips
שבאנג!
רמת קושי: #beginners

כאשר אנחנו מריצים סקריפט שכתבנו ע"י:

$ ./some_ambiguous _script

בברירת המחדל המערכת תריץ את הקובץ באמצעות bash (יש כמה מקרים שבהם זה לא קורה, אבל זה מחוץ לscope של הפוסט), והדרך לשנות את ההתנהגות הדיפולטיבית היא שימוש במשהו שנקרא shebang, הshebang תמיד יופיע בתחילת הקובץ והוא יראה כך:

#!/path/to/interpreter

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

#!/usr/bin/python3
print("This script was executed via python3")


ואנחנו מריצים אותו ע"י:

$ ./cool_script

מה שבעצם מורץ מאחורי הקלעים זה:

$ /usr/bin/python3 cool_script

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

אפשרי גם לקחת את הshebang צעד אחד קדימה ולשלב בפנים ארגומנטים. דוגמה שימושית היא הshebang:

#!/bin/bash -x

אשר תגרום לסקריפט להדפיס כל דבר שהוא עושה (עוזר לדיבוג).

עוד "סוג" של shebang נפוץ הוא הshebang:

#!/usr/bin/env foo

ההבדל בין ה"סוגים" הוא שהאחד תמיד ישתמש באותו הבינארי אשר יהיה בדיוק באותו המקום. בעוד שהשני הוא דינאמי, מאחר והוא משתנה בהתאם לPATH שלנו, אפשרי להסתכל עליו כמשהו ששווה בערך ל:

#!$(which foo)

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

#!/usr/bin/env bash -x

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


קרדיט: תודה לשחר @bananabun האלוף!

#shebang
#execute_script

@bash_tips
להעביר שגיאות ב pipe
רמת קושי: #beginners

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

$ ls /not_exist | wc -l
ls: cannot access /not_exist: No such file or directory
0


כפי שרואים השגיאה הודפסה ישירות לטרמינל ופקודת wc לא קיבלה שום פלט (ערך 0)

איך כן?
בכדי להעביר גם את stderr וגם את stdout דרך ה pipe צריך להריץ את ה pipe בצורה הבאה עם &

$ ls /not_exist |& wc
1 9 50



#pipe
#redirection

@bash_tips
לאגד ולכווץ, tar
רמת קושי: #beginners

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

כדי להכין קובץ tar (שלפעמים מכנים אותו "tarball"):

$ tar -c -f output.tar path/to/dir/

כדי לחלץ את המידע:

$ tar -x -f output.tar

הסיבה לכך שtar כלכך שימושי, היא שכיווץ פועל על קבצים ולא על תיקיות, מה שאומר שכן אפשרי לכווץ את קובץ הtar.

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

$ tar -c -f output.tar path/to/file path/to/dir && gzip output.tar

אפשר לכתוב:

$ tar -z -c -f output.tar.gz path/to/file path/to/dir

וכדי לחלץ את ה tarball המכווץ אפשר לעשות:

$ tar -z -x -f output.tar.gz


קרדיט: תודה לשחר @bananabun האלוף על הפוסט.

#tar
#archives
#compression

@bash_tips