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

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