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

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

תכונה מדהימה יש לבאש שנקראת pipe שמאפשרת לשרשר stdout של פקודה אחת ל stdin של פקודה אחרת לדוגמה פקודת ls מעבירה רשימה לפקודת wc שהיא בתורה סופרת כמה שורות (קבצים או תקיות) יש.
$ ls | wc -l
3

כשמנסים להשתמש באפשרות הזאת בכתיבת סקריפטים אנו עשויים לגלות תופעה מעניינת ואפילו קצת מתסכלת, נניח שכך נראה הקוד שלי בסקריפט
count_files=0
ls | while read -r; do (( count_files++ )); done
echo $count_files

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

$ count_files=0
$ ls | while read -r; do ((count_files++)); done
$ echo $count_files
3
$ ./test.sh
0


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

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

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

@bash_tips
מספר דרכים לדבג סקריפט באש

במידה והסקריפט לא ארוך אפשרי להריץ אותו משורת הפקודה עם דגל -x כך
$ /bin/bash -x myscript.sh

או פשוט להוסיף -x לשאבאנג שבראש הסקריפט שלנו
#!/bin/bash -x

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

set -x
bad code
more bad code
set +x


@bash_tips
מלכודות ואירועים trap ו signals

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

בשביל המצב הזה יש לנו את הכלי trap, מדובר על פקודה שמגיעה כחלק מה bash buildin וכל תפקידה הוא לבצע את הפקודות שמעבירים לה על-פי אירוע (signal) שמתרחש.
קוד לדוגמה
clean() {
echo Remove files...
echo Remove Directories...
}

trap clean EXIT

האירוע שיש לנו הוא EXIT והוא מתרחש מתי שהתוכנית מסיימת לרוץ או אז
מופעלת הפונקציה clean כדי לסדר יציאה מהסקריפט בצורה מסודרת.
מה קורה במידה ותוך כדי ריצה של הסקריפט המשתמש לחץ Ctrl+C? במקרה הזה הפונקציה clean לא תרוץ כי האירוע (signal) שהתרחש הוא לא מסוג EXIT אלא SIGINT.

הנה קוד שתופס את Ctrl+C וישאיר אתכם לכודים בסשן הנוכחי של הטרמינל

HAHA() {
printf "\nsleeping ...zZZ "
}

trap HAHA SIGINT

while true;
do
sleep 10
done

אוקי אבל מאיפה אני משיג את רשימת האירועים שזמינים?
בשביל לקבל את רשימת האירועים שזמינים ניתן להריץ את הפקודה trap -l

$ trap -l
1) SIGHUP 2) SIGINT ...

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

$ man 7 signal

@bash_tips
לקרוא תיעוד לפי קטגוריות man ו whatis

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

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

1 Executable programs or shell commands
2 System calls (functions provided by the kernel)
3 Library calls
4 Special files (usually found in /dev)
5 File formats and conventions eg /etc/passwd
6 Games
7 Miscellaneous
8 System administration commands
9 Kernel routines [Non standard]

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

אפשר לקרוא את כל דפי התיעוד אחד אחרי השני על ידי הרצה של man -a command וכך ברגע שדף התיעוד הראשון נסגר המערכת תציע לנו להמשיך לקרוא את שאר התיעוד מהקטגוריות האחרות שקיימות לאותה פקודה

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

$ whatis signal
signal (7) - overview of signals
signal (2) - ANSI C signal handling

נ.ב. המלצה מאוד חמה לעבור לקרוא את התיעוד של man man

@bash_tips
עבודה עם nano ומספר קבצים

עורך הטקסט nano הפך להיות עורך ברירת מחדל בלא מעט הפצות (אפילו יותר מvim), יש ברשת לא מעט קיצורי מקשים איך עובדים איתו,
טיפ נחמד ש-nano מאפשרת זה לפתוח מספר קבצים במקביל על ידי העברת שמות הקבצים כפרמט או על ידי שימוש ב wildcard למשל כך

$ touch /tmp/test{1..3}.txt
$ nano /tmp/test*

מה שיקרה זה שכל הקבצים שמתאים לתיאור יפתחו ב-nano
מעבר בין קובץ לקובץ נעשה על ידי >+Alt לקובץ הבא ו <+Alt לקובץ הקודם

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

בהקשר של באש
https://devhints.io/bash
סו sed

בשביל למנוע שורות כפולות בשימוש עם sed משתמשים עם דגל -n שמוריד את ה stdout.
sed -n
בהרצה רגילה sed לא משכתב לקובץ את העריכת התוכן שהוא ביצע, בכדי שהוא יערוך את הקובץ עליו הוא רץ מוסיפים את הדגל -i, ניתן להוסיף לפרמטר i שם קובץ חדש כדי שפלט יועבר לקובץ חדש ולא ידרוס את הקובץ הישן
$ sed -i.backupfile 'any/filter/here' myfile.conf

אפשר להשתמש ביותר מביטוי אחד בכל פעם עם sed על ידי שימוש ב ; בין ביטוי לביטוי כך שלמשל אפשרי גם לפלטר שורה שבהערה וגם להסיר שורות ריקות
$ sed ' /^#/d ; /^$/d'

@bash_tips
Forwarded from $~deb
אליאס (alias)

אליאס זו הגדרה שנותנת לנו אופציה להגדיר קיצור לפקודה אחרת
איך זה עוזר לנו ? מקל על חיינו בהרבה !
לדוגמה מהיום אני רוצה ליצור משתמשים במקום
בפקודת sudo adduser אני אקצר ל sudo AU .
כדי לעשות אותם קבועים נשמור אותם בקובץ .bashrc שנמצא בתקיית הבית.
מגדירים כך:
~$ alias <short command> <custom command>

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

מה הפתרון?

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

איך מתקינים?
נתחיל בהתקנה של bash-completion
apt install bash-completion

נעבור להתקנת הסקריפט
נוריד את את תקיית הrepo
נכנס אליה ונכתוב כך
cat complete_alias >> ~/.bash_completion

זהו, מהיום כל alias שנגדיר כדי להפעיל לו השלמה אוטומטית כל מה שנעשה זה נכתוב אחרי ההגדרה בbashrc
complete -F _complete_alias <alias name>
לקבל מידע על החומרה שבמחשב dmidecode, lshw

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

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

להלן דוגמאות
$ sudo dmidecode -s chassis-manufacturer 
ASUSTeK COMPUTER INC.

$ sudo dmidecode -s processor-version
Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz

$ sudo dmidecode -t bios
# dmidecode 3.2
Getting SMBIOS data from sysfs.
SMBIOS 3.0.0 present.

Handle 0x0000, DMI type 0, 24 bytes
BIOS Information
Vendor: American Megatrends Inc.
...

כלי נוסף שמציג מידע על המערכת הוא lswh, בשביל לא לטבוע בכמות המידע אפשר לפלטר את הפלט על ידי שימוש בקטגוריה (Class) של החלק שמעניין אותנו, לדוגמה
$ sudo lshw -C CPU
*-cpu
description: CPU
product: Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
vendor: Intel Corp.
physical id: 11

או להשתמש עם דגל -short שיציג תצוגה מקוצרת שמחולקת לפי קטגוריות
$ sudo lshw -short
H/W path Device Class Description
======================================================
system X556URK
/0 bus X556URK
/0/0 memory 64KiB BIOS
תקציר sed


לפקודה sed לא מעט יכולות ופרמטרים הפעם תקראו על sed ויש מצב שגם תזכרו, למה? בגלל ש-sed ספידה (s.p.i.d.a)
להלן הבסיס של sed עם hint לזיכרון

כל שיש לזכור הוא s.p.i.d.a
למי שרוצה SS שהוא sed spida

s (substitute) - בכדי להחליף תוכן תחת מיקום ספציפי במסמך
sed 's/regex/replace to/g' myfile

p (print) - בכדי לערוך קובץ ולהדפיס למסך
sed '/regex/p' myfile

i (insert) - בכדי להוסיף תוכן מעל מיקום ספציפי במסמך
sed '/regex/ i some text to add above the match' myfile

d (delete) - בכדי למחוק תוכן מהמסמך
sed '/regex/d ' myfile

a (append) - בכדי להוסיף תוכן תחת מיקום ספציפי במסמך
sed '/regex/ a some text to add below match' myfile

להרחיב מעט את הבסיס ולבצע כמה פעולות ויחד לדוגמה, לעשות חיפוש והחלפה
$ sed '{
/regex/ a some text here
/regex/ d
}' myfile


בשביל שזה באמת כיף אפשרי להכניס את רצף הפקודות לקובץ כל פקודת עריכה בשורה חדשה ופשוט לקרוא לו שיחיל את רצף העריכות על הקובץ שלנו
$ sed -f myedits myfile

כמו שאמרנו לא לשכוח להשתמש ב-i לפני שעושים עריכות על קובץ אמיתי

@bash_tips
להריץ פקודות עם sed

פיצ'ר נחמד ש-sed מאפשר זה להריץ פקודות על תוכן שהוא מפלטר, מבנה הפקודה הוא כזה
sed 'action/regex/command /e' file

בא נראה איך זה בעצם עובד, לצורך העניין יש לי קובץ של נתיבים שנראה כך ואני רוצה ליצור את הנתיבים הללו במערכת
$ cat mypath 
# directories for test
/tmp/tests/first
/tmp/tests/second
# any text here
/tmp/tests/mytest

אני רוצה לקבל רק את הנתיבים שקיימים בקובץ ולהריץ עליהם mkdir שיצור לי את רצף התיקיות שברשימה
$ sed 's/ ^\/ /mkdir -p /e ' mypath

אני משתמש ב substitute כדי לקבל רק את השורות שמתחילות ב / ואז מריץ עליהם את פקודת mkdir שתיצור לי תיקייה, כדי להגדיר ל-sed שמדובר על פקודה שאני רוצה להריץ יש לאחר הפקודה e/

וקיבלתי את שבקשתי
tmp/
└── tests
├── first
├── mytest
└── second


@bash_tips
אחסון smarctl, lsblk

בכדי לקבל רשימה ויזואלית של התקני האחסון שקיימים במערכת ותצוגת המחיצות שלהם ניתן להריץ את lsblk, כך ניתן לדעת איזה התקן מעוגן לאיזה סוקט
$ lsblk
...
sda 8:0 0 238.5G 0 disk
├─sda1 8:1 0 260M 0 part /boot/efi
├─sda2 8:2 0 16M 0 part
├─sda3 8:3 0 79.4G 0 part
...

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

דגל -i יתן מידע על החומרה עצמה ואם היא תומכת ב SMART
$ sudo smartctl -i /dev/sda

תגית short מסמלת טסט מקוצר
$ sudo smartctl -t short /dev/sda

ניתן לחזור לבדוק תוצאות לאחר הבדיקה על ידי -l
לייבל devstat יחזיר נתונים חשובים על הדיסק, טמפרטורה, כמות reboots שהדיסק עשה ועוד
$ sudo smartctl -l devstat /dev/sda

לייבל xerror יחזיר את השגיאות מהבדיקה שנעשתה
$ sudo smartctl -l xerror /dev/sda
לייבל selftest יציג סיכום של הבדיקה
$ sudo smartctl -l selftest /dev/sda
להתחיל ברגל ימין Awk

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

$ awk -f myawk.txt  any_file
$ echo “any text here” | awk -f myawk.txt

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

$ echo any text here | awk 'BEGIN {print "Title" } 
pipe quote> END {print "\nTotal: ", NR}'

Title
======
======
Total 1

אז מה שהיה לנו כאן היה די פשוט BEGIN ו-END אלו משפטי ההוראה איפה לכתוב ואז יש בלוק של { print "text" } שאומר מה להדפיס או איזה פעולה לבצע באותו מקום, משתנה NR מחזיק את מספר הרשומות שפקודת awk קיבלה לעבד מה שיוצר לנו סיכום נחמד בסוף הפלט, כמו שאתם רואים עטפתי את את משפטי ההוראה בגרש בודד, הוראות שנכתבים לקובץ אינן צריכות להיות מגורגשות, פשוט וקל יאללה בואו נכניס קצת תוכן משמעותי יותר.
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin


$ awk 'BEGIN {FS=":" ; print "Users\n======" }
pipe quote> { print $1, $6}
pipe quote> END {print "======\nTotal:" NR}' /etc/passwd

Users
======
root /root
daemon /usr/sbin
...
======
Total:46

כפי שבטח הבחנתם כמו בסקריפטינג כל מילה (field) מקבלת איבר משלה שמיוצג על ידי $, בחרנו באיבר הראשון והשישי של כל שורה על ידי $1, $6, וכן כמו בבאש ברירת מחדל התו שמוגדר לחלוקה של מילים לפרמטרים הוא רווח, בשביל להגדיר תו אחר יש את משתנה FS שכפי שרואים מוגדר בשורה הראשונה לחלק על פי תו ":", אפשר להכניס יותר מהוראה אחת בכל סוגריים על ידי נקודה פסיק.
ניתן להשתמש בפקודת printf כדי לעצב טקסט בצורה חכמה יותר.

$ awk 'BEGIN {FS=":";print "Title\n====="}
pipe quote> { printf "%-20s %s \n" ,$1, $6 }
pipe quote> END {print "======\nTotal:" NR}' /etc/passwd

Title
======
root /root
daemon /usr/sbin
...
======
Total:46


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

$ awk 'BEGIN {FS=":";print "Users\n===="}
pipe quote>
$3 > 999 { printf "%-20s %s \n" ,$1, $6}
pipe quote> 
END {print "====\nTotal:" NR}' /etc/passwd

אז מה היה פה, לפני השורה שמציגה את התוכן שמתי תנאי שאומרת תדפיס את התוכן רק אם הערך של משתנה $3 גדול מ 999, למי שלא מכיר ברוב מערכות הלינוקס משתמשים מקבלים id שמתחיל מ1000, שאר שירותי המערכת מקבלים id מ 999 ומטה
זהו הפלט שלי
Users
======
green /home/green
someone /home/someone
======
Total:46

רגע מה, 46 רשומות???
יאפ משתנה NR שומר את כל הרשומות שהוא מקבל ולא את החלקים שפלטרנו, במידה ונרצה את מספר הרשומות שתכלס פלטרנו פשוט נוסיף לשורת התוכן שלנו מונה שהוא בעצם משתנה כלשהו כמו a++ או עם שם יותר מסביר count++ ונחליף את NR במונה שיצרנו

$ awk 'BEGIN {FS=":";print "Users\n===="}

pipe quote>
$3 > 999 { printf "%-20s %s \n" ,$1, $6 ; count++}

pipe quote>
END {print "====\nTotal:" count}' /etc/passwd

Users
====
green /home/green
someone /home/someone
====
Total:2

@bash_tips
Forwarded from HackTips
טיפ #7 - פקודות רצות אחרי סגירת ssh

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

יש כמה דרכים להתמודד עם זה

1. תוכנות terminal multiplexerx
לדוג tmux או screen שעובדים בתצורת לקוח שרת, שאתה מדבר איתו דרך unix socket שנפתח בדרכ ב/run מאפשר לך בקליינט לפתוח כל מיני פקודות, תוכנות שבפועל רצות ב"שרת". בtmux הצירוף ctrl-b ואז d יאפשר לכם לעשות detach לסשן הנוכחי, ואז אפשר לחזור אליו עם tmux attach

2. סיגנל nohup עם &

אתם יכולים להתחיל פקודות עם nohup כדי לדרוס את סיגנל הhup, שזה סיגנל שנשלח לתהליכים כאשר הטרמינל שלהם (pty, tty) נסגר (כולל חיבור ssh)

ה& אומר לbash שמפרסר את האופרטור בתור "תריץ אסינכרוני, תריץ בshell אחר שהוא fork שלי"

3. שיטת job disown

נגיד והפקודה שלכם כבר רצה ואנחנו לא רוצים לעצור אותה, אפשר לדפוק ctrl z כדי להעביר אותה לתור הjobs, משם היא עוצרת, אז נמשיך את הריצה שלה עם bg, עכשיו היא רצה והטרמינל שלנו פתאום חופשי - מפה, נכתוב disown. זה bash builtin (לא תוכנה שנמצאת במקום כמו /usr/bin, אלא פקודה שממומשת בתוך המפרש של bash) שמאפשרת לבאש להסיר את הjobs ששייכים לו ומעכשיו הם יהיו תהליכים לגיטימיים. שימו לב שזה *לא* אותו הדבר כמו nohup, אתה לא משתיק סיגנל סגירת טרמינל ועושה fork לshell-ילד, הjob אחרי הdisown עדיין מכיל טבלת file descriptors של stdin stdout stderr שהם symlinkים לזרמים של... הטרמינל שעשה להם disown. אם התוכנה שלכם מתישהו תנסה לקרוא, לכתוב או אפילו להתלונן על שגיאות למסך - היא עלולה לקרוס. אפשר לטפל בזה מראש ע"י steam redirect לdev null, אבל זה קצת פוגע בפואנטה הרי לא יכלנו לחשוב על זה מראש באופציה 3. ראו הוזהרתם!


לסיום, חידה מגניבה - איך זה שכאשר אתם מרסטים את הsshd, חיבור הssh שלכם עדיין נשאר עובד? מוזמנים לחקור על זה, מבטיח שהתשובה מגניבה! :)
שפת הסקריפט awk

אחרי ראינו את העקרונות והמבנה הכללי של awk הנה עוד כמה דברים נחמדים שאפשר לעשות
נגטיב
$ cat test.awk
!(/Never logged in/) { print $1 }

$ lastlog | awk -f test.awk
Username
someone

רגע יש שם בפלט Username שאינו משתמש במערכת אלא רק כותרת של הפקודה, בא נוסיף חוק שיסיר גם אותו.
!(/Never logged in/ || /^Username/) { print $1 }

להשתמש בתוכן דינמי על ידי משתנים
בצורה זו אפשרי ליצור כלים הרבה יותר גמישים
$ cat test.awk
BEGIN {print var_a, var_b}

$ awk -f test.awk -v var_a=Username -v var_b=Login
Username Login

יש לא מעט משתני סביבה ל awk ראינו בפוסטים הקודמים את NR ואת FS
הנה עוד כמה מעניינים שכדאי להכיר

OFMT - The output format for numbers,
"%.6g", by default.
OFS - The output field separator,
a space by default.
ORS - The output record separator,
by default a newline.
NF - The number of fields in the current input record.

FIELDWIDTHS:
A whitespace-separated list of field widths.
ENVIRON:
An array of the environment variables.
FILENAME:
The name of the current input file.
p.s. filename is undefined inside the BEGIN rule (unless set by getline).

בשביל שיהיה ממש נחמד יש גם פונקציות מובנות
להלן חלקם
I/O
printf - Format and print.
system("cmd") - Execute the command cmd-line,
and return the exit status.

Numeric Functions
int(expr) | atan2(y, x) | sin(x) | cos(x) | sqrt(x) | exp(x) | log(x) | rand()

String Functions
length(s) | toupper(s) | tolower(s) | substr(s, i) | strtonum(s) |match(s, rgx) | index(s, t)

שאר ממתקים שיעבדו לכם כמו כל שפת תכנות C Like
תנאים: רגילים / ternary
לולאות: switch / for / while / do while

@bash_tips
להוריד עם חשבון wget

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

$ wget https://somehost/1.mp3

וכשאנו רוצים להוריד רצף קבצים אנו גם יודעים איך

$ wget https://somehost/{1..100}.mp3

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

$ wget https://somehost/{1..100}.mp3 || exit

@bash_tips
לקרוא json עם jq ואתגרון בקצה

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

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

הקובץ שלי מכיל מערך בודד עם 3 אובייקטים
$ cat my.json
[
{"name":"John", "age":31, "city":"New York"},
{"name":"Smith", "age":13, "city":"Bnei Brak"},
{"name":"Joe", "age":21, "city":"Honolulu"}
]
אני מעוניין לשלוף את שמות האנשים עם jq זה יראה כך

$ cat my.json | jq ' .[].name '
"John"
"Smith"
"Joe"

הסבר
בדומה ל-awk גם jq מקבל את הפקודות שלו בתור מחרוזת (או בתור קובץ נפרד)
כדי לקבל את רשימת האובייקטים המחרוזת שלי ל-jq היא '.[]' שהמשמעות שלה זה תביא בבקשה את כל האובייקטים שקיימים למערך הנוכחי, באותה מידה אוכל להכניס טווח של אובייקטים (.[1:42]) או מספר ספציפי של אובייקט (.[42])
בשביל לקבל את התוכן שאני רוצה פשוט אוסיף את שם הkey הספציפי במקרה שלנו .name כעת jq יעבור באיטרציה על כל האובייקטים וישלוף את הערך של .name

בחירה של מספר keys
ניתן לבחור מספר keys על ידי שימוש בפסיק

$ cat my.json | jq '.[].name, .[].age'
"John"
"Smith"
"Joe"
31
13
21

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

$ cat my.json | jq '.[] | .name, .age'   
"John"
31
"Smith"
13
"Joe"
21

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

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

$ cat my.json | jq '.[] | select(.age >= 20) '
{
"name": "John",
"age": 31,
"city": "New York"
}
{
"name": "Joe",
"age": 21,
"city": "Honolulu"
}

@bash_tips


אתגרון לסיום
איזה משפט ישלוף לי את כל רשימות ההשמעה מהלינק הבא?
https://eco99fm.maariv.co.il/api/v1/public/playlist/12033
תשובות לתגובות
Forwarded from $~deb
tee
זהו כלי שנמצא ברוב ההפצות כחלק מהמערכת
הפקודה משמשת לכתיבה למספר קבצים בנוסף לפלט הרגיל.

איך משתמשים?
echo "hi" | tee file1 file2 file1000

וכמובן אפשר להשתמש בwildcard כדי לכתוב למספר קבצים עם אותו סייומת
echo 'print("hi")' | tee {file1,file2}.py
אמת או שקר

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

$ bool=true
$ declare -p bool
declare -- bool="true"

כפי שאפשר לראות משתנה bool מסומן עם ערך — שזה מקביל לשום ערך מיוחד, מה שמאפיין מחרוזות מה שאומר שהתנאי הבא לא יוכל להתקיים כי ערך של מחרוזת הוא תמיד true
if [[ $bool ]];
then echo print true
fi
print true

$ bool=false
if [[ $bool ]];
then echo print true
fi
print true

בשביל להשוות בוליאן ההשוואה תצטרך להיות או מספרית או של מחרוזת.

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

$ bool=true
$ if ${bool};
> then echo some text here
> fi
some text here
וכמובן תנאי שלילי שלא יציג כלום
$ bool=false
$ if ${bool};
> then echo some text here
>fi

@bash_tips