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

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

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

if [[ 1 -eq 1 ]]
then
echo "I'm in"
fi


התנאי בודק את הביטוי ואם הוא true מה שבתוך התנאי יתממש.

חשוב לציין
, תנאי בבאש לא מחזיר true או false אלא 0 ששוה בעצם לtrue ו 1 ששוה ערך ל false. התנהגות זו שונה מרוב שפות התכנות והסיבה לכך היא שרוב העבודה בבאש היא שימוש בהרבה תוכניות קטנות לבצע מטלה גדולה יותר כך שהתנאי בעצם בודק את הסטטוס קוד שהתוכנית מחזירה, סטטוס קוד 0 שווה לתוכנית שהסתיימה בהצלחה.
(למען הנוחות מכאן והלאה אתייחס ל 0 כ true ו 1 כ false)


קיצורים רבותיי

ישנה דרך מקוצרת לכתוב תנאי

[[ 1 -eq 1 ]] && echo "do it"

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

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

$ cat /tmp/test.sh
#!/bin/bash

if [[ 1 -eq 0 ]]
then
echo "I'm in"
fi
echo $?


$ bash /tmp/test.sh
$ echo $?
0

שימוש בתנאי מקוצר מחזיר סטטוס קוד 1 במידה והביטוי החזיר false

$ cat /tmp/test.sh
#!/bin/bash

[[ 1 -eq 0 ]] && echo "Do it"


$ bash /tmp/test.sh
$ echo $?
1

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


#if
#exit_code
#conditions

@bash_tips
לכתוב תנאים - הרחבה
רמת קושי: #beginners

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

סוגריים עגולים כפולים (( ))
סוג תנאי שעובד רק על מספרים ומתאים לבדיקת פעולות חשבוניות.

if (( 1 == 1 )); then
...

פקודת test
פקודה הבודקת ביטויי השוואה ממש כמו if, מחזירה true ל0 וfalse לכל מה שאינו 0

test 1 -ne 0
test string1 = string2
test -n string
...

סוגרים מרובעים בודדים [ ]
פקודה המבצעת בדיוק את את אותן פעולת שמבצעת פקודת test, ההבדל היחידי בין השניים הוא שפקודת test תגיב לדגלי --help, ו --version

if [ 1 -ne 0 ]
if [ string1 = string2 ]
if [ -n string ]


סוגריים מרובעים כפולים [[ ]]
חשוב לשים לב שמשתמשים ותיקים עלולים לתהות מה ההבדל בין [ ל‏ [[.
ובכן, בעוד הראשון הוא לעתים פקודה בפני עצמה (מקבילה ל־test)

$ whereis [
[: /usr/bin/[ /usr/share/man/man1/[.1.gz 

השנייה היא רק פקודה מובנית ב‏־shell ונותנת לנו יכולות מתקדמות יותר.
למשל, במקום -eq כדי לבחון שיוויון, אפשר להשתמש בסימן שיוויון נורמלי.

if [[ 1 -eq 1 ]]
לעומת
if [[ 1 = 1 ]]

וברמת העיקרון אפשר גם לוותר על תחימה במרכאות של מחרוזות שכוללות תו רווח כי ה־shell יצליח להבין איך להתמודד עם זה.

if [ -f "$filename" ]
לעומת
if [[ -f $filename ]]

כבונוס, ישנו את האופרטור החשוב =~ המאפשר להשתמש בביטויים רגולריים ישירות מתנאי ההשוואה, וכך לחסוך ביטויים לוגיים מורכבים.

אופרטור =~ מה חשוב לדעת?
חשוב רק לציין שבמידה ומשתמשים באופרטור ההשוואה =~ יש הבדל בין משתנה העטוף בגרשיים (להלן מגורגש) לכזה שאינו, ברגע שמעבירים לתנאי משתנה מגורגש הביטוי מחפש את כל מחרוזת הטקסט שבמשתנה בצורה גולמית ולא מנסה להשוות את המשתנה כביטוי regex

$ cat file.txt
111+.*pdf{}[]$

$ [[ $(cat file.txt) =~ "pdf{}" ]] && echo match
match

$ [[ $(cat file.txt) =~ pdf{} ]] && echo match
$ echo $?
1

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


קרדיט: תודה רבה לתומר @tomer מקבוצת @linux_il על הפוסט! (אז מה אם הוא השתנה מעט)
למי שלא מכיר קבוצת לינוקס ישראל היא אחת הקבוצות היותר טובות והיותר פעילות בטלגרם (כל עוד לא מדברים על קאלי).


#[
#if
#test
#regex
#conditions

@bash_tips
שימוש בsed עם extended regex
רמת קושי: #beginners

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


בכדי לאפשר extended regex לsed ניתן להשתמש בדגל -E או -r

$ sed -E 's/something_(aaa|bbb|ccc)//g


#sed
#regex
#extended_regex

@bash_tips
עושים שיפט
רמת קושי: #advanced

אנו מכירים את צורת העברת פרמטרים לסקריפט באש, כל פרמטר מקבל מספר וניתן לגשת לפרמטרים לפי האינדקס שלהם על ידי $
$ cat test.sh
echo $1
echo $2
echo $3

$ bash test.sh one two three
one
two
three

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

$ cat test.sh
echo "$*"

$ bash test.sh one two three
one two three


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

$ bash test.sh base64 one two three
base64 one two three

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

action=$1
echo "${*:2}"


דרך נקייה יותר לעשות את אותו הדבר היא להשתמש בפקודת shift
פקודת shift היא כלי שמגיע כחלק מ bash builtin ומה שהיא נותנת זה "להזיז" את הפרמטרים מספר צעדים קדימה כך שהפרמטר הראשון יוסר מהרשימה ומה שהיה לפני כן במשתנה $2 יהפוך להיות $1

$ cat test.sh
shift 1
echo $1
echo $2
echo $3

$ bash test.sh one two three
two
three

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

action=$1
shift 1
echo "${*}"

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


#shift
#scripting
#function
#parameters

@bash_tips
מפות ומערכים
רמת קושי: #beginners

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

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

$ mapfile < myfile
$ echo ${MAPFILE[@]}
one
two
three

במידה ונעביר שם של משתנה התוכן יכניס למשתנה אותו העברנו

$ mapfile mylist < myfile
$ echo ${mylist[@]}
one
two
three

כפי שניתן להעביר קובץ, ניתן גם להעביר תוכן של משתנה למערך על ידי שימוש ב here-string

$ mapfile mylist <<< "$myvar"

$ echo ${mylist[@]}
one
two
three


#array
#mapfile
#readarray

@bash_tips
מערכים ומפות read
רמת קושי: #beginners

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

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

$ myline="one two three"
$ read -a mylist <<< "${myline}"
$ declare -p mylist
declare -a mylist='([0]="one" [1]="two" [2]="three")'

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


#read
#array

@bash_tips
אני רוצה לברך את כל תושבי הערוץ
בחג שמח ובשנה טובה מבורכת ועשירה


שתהיה שנה של אושר, שגשוג ותקווה.
שנה של אמונה בתהליך, ושקושי הוא המקום בו אנו משתפרים.
שנה של המון הצלחות קטנות מרוממות.
שנה של מניית הצלחות במקום כשלונות.
שנה של החלטות קטנות שמשיגות תוצאות ענקיות.
שנה של אנושיות סובלנות ואהבה לכולנו.
לקבל ערך indirect references
רמת קושי: #advanced

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

$ cat test.sh

key="value"
myvar="key"

echo "\${key} = ${key}"
echo "\${myvar} = ${myvar}"
echo "\${!myvar} = ${!myvar}"


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

$ bash test.sh
${key} = value
${myvar} = key
${!myvar} = value


מה היה לנו כאן?
שני השורות הראשונות דיי ברורות לנו, אנו קוראים למשתנה שמכיל מחרוזת והמחרוזת מודפסת, בשורה האחרונה לעומת זאת מתרחש החלק המעניין, אנו מדפיסים את השם של המשתנה (myvar) עם !, וזה בעצם שולח את באש לחפש אם קיים לה בזיכרון שם של משתנה בשם של המחרוזת (key), במידה והתשובה היא כן אנו נקבל את הערך של אותו משתנה (value) כאילו קראנו למשתנה עצמו.

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

$ cat test.sh
read -p "please enter exist ENV vartiable name: " var
echo "variable name ${var}: ${!var}"

$ bash test.sh
please enter exist ENV vartiable name: SHELL
variable name SHELL: /bin/bash


#indirect_references
#indirect_expansion

@bash_tips
👍1
להעביר מערכים לפונקציה
רמת קושי: #advanced

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

$ cat test.sh
myFunc(){
echo $1
echo $2
}
myFunc one two

$ bash test.sh
one
two

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

myFunc(){
echo $1
echo $2
}
myList=(one two)
myFunc ${myList[@]}

$ bash test.sh
one
two

נוכל לנסות לאסוף את כל הערכים שמגיעים לפונקציה ולהגדיר אותם כמערך על ידי שימוש ב ${@} בפונקציה, אבל זה יכול לעבוד רק בתנאי שאין לנו עוד ערכים אחרים להעביר לפונקציה

myFunc(){
echo ${@}
echo $2
}
myList=(one two)
myFunc ${myList[@]} new_parameter

$ bash test.sh
one two new_parameter
two

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

פתרון 1: העברת מחרוזות
הפתרון הראשון הוא פשוט להעביר את המערך כמחרוזת ואז בפונקציה עצמה ליצור מערך חדש על ידי העברה של המחרוזת ל read כפי שראינו בעבר.

myFunc(){
read -r -a arr1 <<< ${1}
read -r -a arr2 <<< ${2}
declare -p arr1 arr2
}
arr1=(aaa bbb ccc)
arr2=(ddd eee fff)

myFunc "${arr1[*]}" "${arr2[*]}"

$ bash test.sh
declare -a arr1=([0]="aaa" [1]="bbb" [2]="ccc")
declare -a arr2=([0]="ddd" [1]="eee" [2]="fff")

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

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

$ cat test.sh
myFunc(){
func_arr1=( "${!1}" )
func_arr2=( "${!2}" )
declare -p func_arr1 func_arr2
}

arr1=("aaa" "bbb space" "ccc")
arr2=("ddd" "eee" "fff")
myFunc arr1[@] arr2[@]

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

$ bash test.sh
declare -a func_arr1=([0]="aaa" [1]="bbb space" [2]="ccc")
declare -a func_arr2=([0]="ddd" [1]="eee" [2]="fff")

#read
#array
#function
#arguments
#indirect_references

@bash_tips
פוסט מעולה של ינון פרק
הערות על הפוסט בבקשה בערוץ של ינון
Forwarded from ToCode
# טיפ באש: השתמשו ב null glob כשצריך
את הלולאה הקצרה הזאת כתבתי בסקריפט בלי לחשוב יותר מדי:

#!/bin/bash -e

for json_file in /data/*
do
echo "Importing JSON file $json_file"
python import_json.py "$json_file"
echo "-- Done"
done


ולמה בלי לחשוב? כי אם הסקריפט import_json.py ינסה לפתוח את הקובץ שהוא מקבל לקריאה, והתיקיה ריקה, אנחנו נקבל את השגיאה:

FileNotFoundError: [Errno 2] No such file or directory: '/data/*'


הסיבה לשגיאה המוזרה היא ש bash מפענח את הביטוי /data/* להיות הטקסט הפשוט /data/* ולא רשימת שמות הקבצים בתיקיה שם - בגלל שאין קבצים באותה תיקיה. אפשר לראות את זה בדוגמה פשוטה יותר בלי לולאה או פייתון אם פשוט נכתוב את שתי הפקודות:

# this will print all the files in /etc
$ echo /etc/*

# but this will just print "/data/*"
$ echo /data/*


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

הדרך הכי קלה לצאת מזה היא להשתמש ב nullglob, שזה אומר שאם הביטוי לא מתאים לכלום אז באש יחזיר מחרוזת ריקה. נסו את זה:

$ shopt -s nullglob
$ echo /data/*


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

#!/bin/bash -e

shopt -s nullglob

for json_file in /data/*
do
echo "Importing JSON file $json_file"
python import_json.py "$json_file"
echo "-- Done"
done


הפעם הסקריפט לא ינסה לפתוח קובץ עם השם המוזר /data* ופשוט לא יבצע אף פעם את גוף הלולאה כי באמת אין קבצים שמתאימים לביטוי.
פוסט יפה ומפורט מאוד של טל בן שושן על באש
רמת קושי: #beginners

מוזמנים להמליץ על עוד תוכן וקבוצות ישראליות איכותיות

@bash_tips
לחפש כשמכווץ zgrep
רמת קושי: #beginners

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

$ zgrep crash /var/log/syslog*
/var/log/syslog.4.gz ...
/var/log/syslog.5.gz ...
/var/log/syslog.9.gz ...
...


#archives
#zgrep
#grep
#zip

@bash_tips