לדלג לתוכן

מילון BASH

`command --help`

בשביל עזרה בחיים...

wsl

explorer.exe

  • כדי להריץ קובץ עם ווינדוז, כמו execution רגיל של הקובץ בווינדוז, נעשה explorer.exe filename-or-path
  • חשוב לשים לב שאם נותנים לו path, הוא צריך אותו בפורמט DOS, כלומר שצריך להריץ [[מחברות קוד/Bash&Linux 🐧/מילון BASH#dos2unix|dos2unix]] על ה-path

clip.exe

  • כדי להעביר פלט לקליפבורד של ווינדוז
  • משתמשים בפייפ <commands> | clip.exe

misc

"#" comment

אפשר להשאיר הערות בקוד באמצעות #

this-is-code 
# this is a comment 

  • לפעמים נראה בקבצי קונפיגורציה כל מיני סניפטים של קוד שמוחרגים מההרצה באמצעות # (הם בעצם הערות) - הכוונה היא לתת לנו "אפשרויות מוכנות" (או דוגמאות) להגדרות שאפשר להפעיל ע"י מחיקת ה-#
    • למשל, ב-bashrc של אובונטו יש את הקטע הבא:
      # some more ls aliases
      #alias ll='ls -l'
      #alias la='ls -A`
      #alias l='ls -CF'
      

man

נותן manual לפקודה, יותר טוב מהלפ. man <command>

type

type <command> כדי לדעת מאיזה סוג הקומנד. יש 4 סוגים בגדול: אקסקיוטבר, shell bult-in, shell function (env), alias

which

which <executable> יש לנו כל מיני מקומות עם אקסקיוטבלז ב-path. אם אנחנו רוצים לראות איפה יושב אחד שאנחנו מפעילים (במערכות גדולות יתכנו כפילויות), אנחנו משתמשים ב-which

sleep

sleep n מחכה n שניות ואז עושה exit

history

מראה את הפקודות האחרונות, שעולות כשעושים חץ למעלה - היסטוריית הפקודות שלנו נשמרת בכמה שלבים: 1. בתור התחלה, המידע נשמר בקובץ היסטוריה זמני של ה-Session שלנו (לדעתי מקבצי ה-temp של המערכת). 2. כשהסשן נסגר (או כשאנחנו שומרים ידנית), המידע הזמני נשמר ב-home בקובץ בשם .bash_history כברירת מחדל, באש שומר את ה-100 פקודות האחרונות, אפשר לשנות את הערך הזה ב-bashrc

  • כשאנחנו מריצים shell session הוא טוען את קובץ ה-bash_history ומוסיף אותו להיסטוריה "הזמנית", כאשר כל פקודה חדשה תסופח לתחתית הקובץ הזמני ללחוץ על חץ-למעלה נותן לנו את השורה האחרונה בקובץ הזמני, ולאחר מכן כל לחיצה תעלה שורה. ככה, יש לנו גם היסטוריה "רחוקה" וגם היסטוריה "עדכנית" באותו קובץ פעיל

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

    history -r [filename]: תטען קובץ טקסט כלשהו להיסטוריה הפעילה שלנו. אם לא נכתוב ארגומנט ל-filename, הפקודה תטען מחדש את הקובץ הרגיל, .bash_history

    history -c תנקה את ההיסטוריה הפעילה שלנו

    history -w [file]
    Writes the current session history to a file.

    history -a Appends the current session’s history to the default history file (~/.bash_history).

    history -n Reads new history lines from the file without overwriting existing session history.

  • לא לשכוח מ-[[מילון BASH#history expansion#|history expansion]]

חיפוש בהיסטוריה

  • Ctrl+r כדי להיכנס למצב חיפוש בהיסטוריה אנטר כדי להריץ את הפקודה שנבחרה, Ctrl+r שוב ושוב כדי לדפדף Ctrl+j כדי להעתיק את הפקודה שנבחרה לקומנד ליין ctrl+g or c כדי לצאת מהחיפוש

![[Pasted image 20250124180056.png|500]]

script

script pathtofilename 'מקליט' את כל הסשן ומייצא אותו לקובץ שמצוין אם לא נבחר שם, לקובץ יקראו typescript

/usr/share/dict

לא ממש מילון BASH אבל ילך לאיבוד בלינוקס הכללי. בכל מערכת לינוקס יש כמה קבצי מילון! שיושבים ב-path של הכותרת. זה שימושי למקרה שאנחנו רוצים לחפש מילים מתאימות לאיזשהו צורך. למשל: אם אנחנו רוצים להשתמש בכדי למצוא פתרונות אפשריים לתשחץ... grep -i '^..j.r$' /usr/share/dict/words

systemctl

  • ממשק לשליטה בפונקציות של Systemd (בגדול מנהל התהליכים בלינוקס)
  • כיבוי, השהיה, ואתחול של המחשב (restart): systemctl suspend systemctl poweroff systemctl reboot

  • הפעלה של תהליכים sudo systemctl enable x sudo systemctl start x

time

  • בודק כמה זמן לקח להריץ פקודה מסוימת
  • טוב בשביל אופטימיזציה time ( commands ) לשים לב לרווח אחרי ולפני הסוגריים

fzf

  • תוכנה, צריך להוריד אותה
  • יוצרת תפריטים. ברירת המחדל אם נעשה fzf יהיה לתת את כל הקבצים שתחת ה-path הנוכחי (./)
  • בד"כ נרצה לעשות לתוכה pipe עם פקודה שנותנת ערכים מופרדים בשורות (או תו אחר שנגדיר), או שאפשר לתת לה ערך עם האפשרות --query
  • הפלט של fzf יהיה הפריט הנבחר
  • אפשר להריץ את fzf עם אפשרויות מיוחדות:
    • בכל אפשרות כזו, המשתנה {1} שווה לערך של השורה המסומנת ב-fzf
  • fzf --preview 'command' כדי להגדיר מה נראה בפריוויו fzf --preview 'cat {1}' בשביל לראות פירוט של קובץ

  • fzf --bind '<key>:<command> fzf --bind 'ctrl-e:accept' כדי לאשר בחירה עם בינד fzf --bind 'ctrl-e:execute(<command>) כדי לבצע פקודת של מסוימת עם בינד fzf --bind 'ctrl-e:abort' לסגור את fzf fzf --bind 'ctrl-e:toggle-preview וכו'

  • fzf --query=<string> להתחיל מחיפוש מסוים

  • fzf --except=<keys> להגדיר דלימיטר שהוא לא שבירת שורה עבור רשימת הערכים
  • fzf --multi=<MAX> כדי לאפשר בחירות מרובות עם טאב או שיפט טאב
  • fzf --filepath-word כדי שהחיפוש יכבד פאת'ים שנכתוב*
  • אפשרויות מעניינות ב---help לגבי עיצוב הממשק בדרכים שונות, הגדרת דלימיטרים לקלט ולפלט,

הרשאות

id

id נותן מידע בסיסי על היוזר שאנחנו נמצאים עליו uid = המספר של היוזר, שהוא המזהה שלו לפני שמוצמד לו שם רגיל gid = primary group id היוזר עשוי להיות בעוד קבוצות של הרשאות אבל זה הקלאס הבסיסי שלו groups= יפרט איזה עוד קבוצות יש ליוזר

המידע הזה מגיע מקובץ טקסט. המידע על יוזרים יושב ב-/etc/passwd והמידע על קבוצות ב-/etc/group id -g בשביל הקבוצה של הויזר בלבד id -gn בשביל המספר של קבוצה של היוזר id -n באופן כללי בשביל לקבל מספר של תכונה של היוזר במקום את הכותרת שלה

chmod

כשאנחנו אומרים לשנות את ה-'מוד' של הקובץ, הכוונה היא לשנות את ה-permissions שלו אפשר להפעיל chmod על הקובץ אם אתה הבעלים שלו, או אם אתה הסופריוזר אפשר לציין את השינוי ב-octal numbers או ב-symbolic רפרשנטיישן

אוקטאלי:

![[Pasted image 20250124182731.png|500]]

בגדול, בגלל שכל חלק בהרשאות הוא 3 אותיות (rwx כאשר כל אחד יכול להתחלף ב'-' כדי לסמל שהוא לא פעיל), ובגלל שכל ספרה אוקטאלית מייצגת 3 אותיות בינאריות, אנחנו ממספרים את המצבים השונים האפשריים בטבלה ומצמידים להם ערך מ-0 עד 7 (באוקטאלי יש את כל המספרים שאפשר להרכיב עם הספרות 0-7) ![[Pasted image 20250124183040.png]]

לצורך העניין, chmod 777 pathtofile יעשה שגם לבעלים, גם לקבוצה שלו וגם לכל השאר יהיו את כל ההרשאות (rwx-rwx-rwx) לקובץ המצויין

סימבולי מציינים את השינוי עם 3 פרמרטרים: 2. על מי הוא חל

![[Pasted image 20250124183345.png|500]]

אם לא מציינים, הטרמינל מניח a

  1. מה השינוי + אומר שמוסיפים הרשאה - אומר שמורידים הרשאה = אומר שההרשאה שנגדיר היא היחידה שתהיה (וכל השאר יימחקו)

  2. מה ההרשאה שמגדירים בהתאם לפורמט שלמדנו: r, w, x בשביל קריאה, כתיבה והרצה בהתאמה

לכן, למשל chmod a+x אומר "לכולם מתווספת האפשרות לעשות execute לקובץ" - את הפאת' לקובץ מציינים בסוף, ככה chmod a+w pathtofile

להפוך קובץ ל-Executable: chmod a+x ./filename (בתיקיה של הקובץ) לפעמים +x בלי a, זה לא משנה, שניהם אמורים לתת הרשאת execution לכל סוגי המשתמשים

עוד דוגמאות

![[Pasted image 20250124183723.png|500]]

umask

פקודה שקובעת מה ההרשאות שיוגדרו לקבצים כברירת מחדל, כשהם נוצרים משתמש ב-octal notation (התוויה אוקטלית) כדי להגדיר "mask" של ביטים, שהיא 'הסרה' של ערכים שמגדירה את ההרשאות שיוותרו: ![[Pasted image 20250124184428.png|500]] הסבר: ה-'מסיכה' בנויה על רצף של 4 שלשות. ה-000 הראשונים לא משנים כרגע. השאר הם rwx של היוזר, הקבוצה והשאר כמו שלמדנו אנחנו מגדירים את umask עם 4 ספרות, שכל אחת היא התייחסות אוקטלית לשלשה בינארית שכזו. למשל umask 0002 אומר בעצם: "שלוש שלשות בינאריות "ריקות", ועוד אחת במצב 2". מצב 2, כמו שלמדנו בטבלה למעלה, הוא 010 וזה בעצם מסמן w (הסדר הוא rwx) המסיכה גורעת הרשאות, ולכן לקובץ שהגדרנו עם umask 0002 בעצם שללנו הרשאות w (כתיבה) עבור ה-others (בקיצור, בכל מקום ש-1 'פוגע', הערך המתאים עבורו נגרע מההרשאות) umask כדי לבדוק מה מוגדר לנו כרגע umask xxxx כדי להגדיר מחדש

הסבר על המעבר מבינארי להקסדצימלי/אוקטאלי: העניין הוא איך אנחנו "ממספרים" את האיברים השונים שאפשר לבטא אם יש לנו "תא" של מידע והערכים שלו הם אוקטליים, הוא בעצם יכול להיות כל ספרה מ-0 עד 7 (8 ספרות בסה"כ, אוקטלי). כדי למספר 8 "אפשרויות", כל אחת באופן ייחודי, אנחנו צריכים 3 תאים "בינאריים" 000 001 010 100 011 110 101 111 כל אחד מאלה "מוצמד" לאחד הערכים האוקטליים אותו דבר עם ערכים הקסדצימליים רק לפי base 16: כל "דאטה" שבנויה מרצף של 4 תאים בינאריים (נניח 1010) אפשר לבטא באמצעות אות אחת בסולם ההקסדצימלי, שהיא פשוט האות ש-"הוצמדה" לקומבינציה הספציפית הנתונה. אנחנו משתמשים בערכים הקסדצימליים לבחירת צבעים, כי כל צבע הוא 8 ביטים של r, של g ושל b -- אז מקצרים רצף בינארי של 24 תווים ל-6 תווים הקסדצימליים (24/4)

ומה הסיפור עם השלשה הראשונה? שמנו לב שזה לא umask xxx אלא umask xxxx השלשה הראשונה מאפשרת לנו להגדיר במסיכה כמה הרשאות ייחודיות: umask 4000 (100 000 000 000) הרשאה שנקראת setuid bit היא לא באמת עושה מה שהבינארי שלה אומר (מורידה הרשאות read), אלא מופעלת על קובץ אקסקיטובל כדי לעשות שהוא תמיד ירוץ עם 'effective user id' של הבעלים ולא של היוזר שמפעיל אותו. זה שימושי כדי שתוכנות מסוימות תמיד ירוצו כ-superuser. אנחנו לא רוצים לחלק את ההרשאה הזו בלי סיבה מפורשת.

umask 2000 setguid bit משמש כדי שההרשאות על תיקיה יהיו לפי הבעלים שלה ולא לפי היוזר שפותח בה קבצים. זה דווקא שימושי לתיקיות משותפות: נגדיר את ההרשאה של תיקיה להיות setgid bit כדי שכל מי שפועל בתוכה ייצור קבצים עם הרשאות שמתאימות לקבוצה שהתיקיה משויכת אליה, ולא עם ההרשאות הספציפיות שלו

umask 1000 sticky bit משהו ישן של unix. לא עושה כלום על קבצים. על תיקיה, זה עושה שלא יהיה ניתן למחוק בה קבצים או לשנות להם שמות, אלא אם היוזר הוא הבעלים או הסופריוזר

(בקיצור, ל-000 הראשון ב-umask יש 3 מצבים, 001, 010, 100 שמגדירים את שלושת ההרשאות המיוחדות האלה)

אפשר להגדיר את ההרשאות האלה גם סימבולית, ויש להן ביטוי ב-[[לינוקס - כללי#file attributes#|file attributes]] - ההרשאות setuid ו-setgid שתיהן מסומנות ב-s - ההרשאה sticky bit מסומנת ב-t - שניהם יופיעו במקום x - איך נדע אם s מתייחס ל-setuid או ל-setgid? זה די קל: setuid רק על קבצים ו-setgid פועל רק על תיקיות - עוד הבדל: setuid יהיה על השלשה הראשונה של rwx בעוד ש-setgid יהיה על השלשה האחרונה (אחד מתייחס לבעלים ואחד מתייחס ל-world)

![[Pasted image 20250124191101.png|500]] umask פועל רק לסשן אחד, צריך להשתמש במשתני סביבה כדי לעשות אותו קבוע

su

su [-l] [user] פקודה שמשמשת כדי להתחיל shell session חדש מיוזר אחר, לפי מה שנכתוב הדגל -l אומר שאנחנו רוצים לעשות לוגין ליוזר הזה, כלומר להזין ססמה, לטעון את ה-bashrc שלו ולעבור לתיקיית ה-Home שלו אפשר לכתוב גם su - (קו בלי l) וזה אותו דבר אם לא מגדירים יוזר - הטרמינל מניח שאנחנו מדברים על ה-superuser

אפשר לדעת שאנחנו על הסופריוזר כשיש לנו # בסוף של ה-shell prompt במקום $ exit כדי לצאת בחזרה לסשן הקודם

su -c 'command' כדי לשלוח קומנד אחד כיוזר אחר בלי להחליף סשן להשתמש ב-quotes כי אנחנו לא רוצים שהפקודות יתרחבו בטרמינל הזה! רק בטרמינל של היוזר שישגר בפועל את הפקודה (הן יעברו אליו בלי הגרשיים ולכן יתרחבו אוטומטית כרגיל)

איך מגדירים ב-c איזה יוזר?

sudo

חשוב להבין ש-sudo זה לא "הפעל כמנהל" במושגים של ווינדוז. זו פקודה שנועדה בדיוק כדי שיוזרים יוכלו לבצע פעולות מסוימות מבלי להיות המנהלים ה-admin מגדיר פקודות מסוימות שיוזרים מסוימים יכולים להפעיל עם הרשאות superuser, באמצעות sudo, מבלי שידרשו לססמה של superuser כש-sudo מבקש ססמה, הוא מבקש את הססמה של היוזר הפעיל, ולא של הסופריוזר! - לא צריך לעטוף פקודות שעלולות להתרחב בגרשיים, כי sudo לא פותח סשן חדש של הטרמינל - אפשר לעשות sudo -i כדי לפתוח סשן חדש של סופריוזר באמצעות sudo - sudo -l כדי לראות איזה הרשאות יש עם סודו במערכת - ההרשאות של sudo מוגדרות ב-[[לינוקס - כללי#/etc/sudoers#|/etc/sudoers]] (הלינק הזה כולל הסבר על sudo vs su בהיסטוריה של לינוקס)

chown

CHange OWNer מאפשר לנו לשנות את ה-owner של קובץ, ולשנות את ה-group owner של קובץ (הקבוצה שהקובץ שייך לה). chwon <owner><:group> path-to-file-or-dir

  • למשל chown bob:users log.txt ישנה את הבעלות על log.txt להיות של היוזר bob ושל הקבוצה users
  • אפשר לעשות chown bob: log.txt, אם שמנו : אחרי היוזר ולא כתבנו את השם של הקבוצה, ההרשאות ילכו ל-login group של היוזר שציינו (כלומר לקבוצה העיקרית שהוא משתייך אליה)
  • אפשר לעשות chown :users log.txt כדי להגדיר רק את ה-group owner

יש פקודה ישנה בשם chgrp שמשנה ספציפית את הקבוצה, זה בגלל שבעבר chown שינתה רק יוזר (כבר לא)

groupadd

פקודה ליצירת user group חדשה. sudo groupadd groupname

usermod

sudo usermod -a -G groupname usertoadd -a אומר append -G אומר Group המשמעות של הפקודה היא "להוסיף את היוזר שצויין לקבוצה שצויינה בקובץ ב-/etc/group"

passwd

משנה ססמה ליוזר שיש לנו גישה אליו passwd user אם נכתוב passwd זה יפעל עלינו רק הסופריוזר/הרשאות סופריוזר מאפשרים לתת ארגומנט של יוזר אחר אפשר לקבוע באמצעות הפקודה הזו (כסופריוזר) דברים כמו מס' נסיונות, אקספירציה של הססמה וכו'. (לקרוא ב-man)

shadow-utils

הפקודות passwd, addgroup, usermod הן חלק מסויטה בשם שאדו יוטילס הנה עוד פקודות מהסויטה הזו: ![[Pasted image 20250124195907.png|500]]

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

ליצור תיקיה - mkdir

mkdir path

אפשר גם כמה mkdir dir1 dir2 dir3

ליצור קובץ - touch

touch pathtofilename או בשביל ליצור אותו עם טקסט מסוים מראש: echo text

למחוק קובץ - rm

rm path rm -rf ~/.local/share/Trash/* -f, --force Ignore nonexistant files, and never prompt before removing.-r, -R, --recursive Remove directories and their contents recursively.`` רקורסיבי = למחוק תיקיה ואת כל מה שבתוכה

^ דוגמה לדגלים.

להריץ קובץ

פשוט מכוונים את הטרמינל לשם שלו בתיקיה שלו. אם אנחנו בתיקיה, אז ./filename

להעתיק קובץ או תיקיה - cp

cp path destination cp path -r destination כדי שיהיה רקורסיבי (כל עוד יש מה להעתיק איי גס) ![[Pasted image 20250124010424.png]]

להזיז קובץ ולשנות את שמו - mv

  • שניהם עם mv
  • אם מגיעים להשתמש בזה אז כנראה sudo mv
  • sudo mv filepathwithformat destinationpath
  • sudo mv filepathandcurrentname samepathwithnewname
  • כאשר אנחנו מזיזים קובץ או משנים את שמו כך שתהיה התנגשות בין שני קבצים (אותו שם): ברירת המחדל היא overwrite בלי לשאול אותנו (אם לא משתמשים בדגל כלשהו) mv -i עושה שישאל אותנו לפני שכתוב mv -f עושה שישכתב, כמו ברירת המחדל mv -n עושה שלא ישכתב ואם יש כפילויות יכשל mv -b עושה שיגבה שכתובים (ישנה להם את השם)

ניווט:

ls

  • ls - מפרט מה יש בתיקיה
  • ls -a כדי לראות גם קבצים חבויים (מתחילים בנקודה)
  • ls -l כדי לקבל רשימה בלונג פורמט ![[Pasted image 20250123170117.png]] הכי חשוב שלא כתוב כאן: ls -i כדי לראות ברשימה את מס' ה-inode של הקבצים. ככה אפשר לראות איזה קבצים הם הארדלינקים שונים של אותו ה-Inode (שכפולים)

הסבר על העמודות ב-long format: ![[Pasted image 20250123170237.png]]

  • sudo - מזמן מתאבק סומו
  • cd - זה ללכת לתיקיה לפי ה-path
  • cd .. - מחזיר אחורה בתיקיה - אפשר עד 3 ברצף ככה: cd ../../..
  • pwd - מדפיס את הפאת' הנוכחי בצורתו המלאה
  • pwd -P כדי לקבל את ה-physical path, כלומר בלי symbolic links

אפשר גם ls path1 path2 בשביל רשימה נפרדת לכל פאת'

קיצורים:

  • ~ אומר home/user
    • לעשות cd ~ יקח אותי ל/home/tony
    • אם אתה מחובר לטרמינל כרוט/אדמין, זה לוקח ל/root
    • כאשר משתמשים ב-sudo, ~ לא עובד. צריך להשתמש ב-$HOME
  • הבהרה חשובה: זה שאני משתמש עם sudo capabilities לא אומר שאני משתמש בטרמינל כרוט. בשביל להפעיל את זה צריך להיות משתמש עם sudo ולכתוב sudo su או sudo -i.
    אז נניח בשביל לפתוח את downloads אני יכול פשוט לעשות ~/downloads
    מבחינת ה-, לזכור שכולם חוץ מווינדוז משתמשים בלוכסן שהולך קדימה (ביחס לאנגלית).
  • לעשות . אומר "התיקיה הנוכחית"

    • כדי להריץ קובץ, ./filenmame
    • לא עובד עם sudo, צריך במקום לעשות $PWD
    • לעשות / אומר root

    • הסיבה שלעשות /home מצליח להפנות ל-home partition ולא ל-root, היא ש-home יושבת בroot בכל מקרה. אם יצרנו partition נפרד בשבילה בעצם יצרנו link - נוכל לראות את home בתוך ה-root partition, אבל להיכנס יעביר אותנו ישר ל-home partition

    • לעשות .. אומר parent directory
  • לעשות + או - אומר תיקיה אחת אחורה/קדימה (ביחס להיסטוריה)

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

cd ../..

אומר ללכת לתיקיה הקודמת-קודמת. וכו'.

השלמה אוטומטית (completion)

  • אפשר להקליד חלק מ-Path, ללחוץ TAB ולקבל הצעות לאן להמשיך. זה כמו לעשות ls בלי לשלוח את הפקודה ככה שאפשר פשוט להמשיך לכתוב.
  • עובד גם עם יוזרים אחרי ~, עם משתנים אחרי $ ועם הוסטים אחרי @
  • כדי שהרמז יצליח חייבת להיות התאמה ![[Pasted image 20250124170137.png]]

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

קיצורי מקלדת

![[Pasted image 20250124165430.png|500]]

![[Pasted image 20250124165702.png|500]]

kill = copy yank = paste

file

בלינוקס, קובץ לא חייב סיומת של פורמט (וגם קובץ בשם .txt לא חייב להיות קובץ טקסט). כדי לבדוק פורמט של קובץ עושים file pathtofile עקרון Unix חשוב: everything is a file

ידוע גם כקיצור דרך ``

ln -s /Users/jessicawilkins/Music ~/my_music
לשים לב שמתחילים מה-target (מה שמלנקקים אליו) ורק אחריו בוחרים פאת' ל-symlink שניצור בשביל symbolic link (קיצור דרך רגיל) בלי הs- בשביל hard link

ELI5: The way data is structured in Linux consists of 3 parts: (1) files, (2) index (called inodes), and (3) raw data. inodes tell your programs which segment of raw data make up a file. Basically it looks like this File ==> inodes ==> raw data Hard link is multiple filessame inode. Modify any of hard-linked file change the inodes and raw data, hence change all the files. When the last hard-linked file is deleted there's no more reference to the inode, thus the file is considered deleted. Symbolic link is reference to the file. Like this Symbolic ==> file ==> inodes ==> raw data. Modify symbolic link will change the file. However, deleting all symbolic link will not delete the file, but delete the file will delete all the symbolic links.

הארד לינק זה דבר די ישן של unix, יותר מודרני להשתמש בסימבוליק לינק כשאנחנו עושים הארד לינק, אנחנו מוגבלים למערכת הקבצים שלנו (אי אפשר לרפרר לאחת אחרת), ואין הבדל בין הקובץ למה שהוא מרפרר אליו. הלינק הוא פשוט עוד כתובת של המידע. גם הקובץ המקורי הוא בעצם hardlink ל-inode של הקובץ. להוסיף symbolic link זה להוסיף עוד שכבה - אנחנו מרפררים לקובץ שהוא מיקום אחד של ה-inode. לערוך את ה-symlink לא יערוך את הקובץ המקורי, אבל לערוך את הקובץ המקורי ישנה את כל ה-symlinks (ב-ls -l תמיד נראה שלקובץ יש לפחות הארד לינק אחד, כי הקובץ עצמו הוא הארד לינק) בנוסף רק סימבוליק לינק עובד על תיקיות. הארד לינק זה לקבצים בלבד.

stat

stat pathtofile כדי לקבל את כל המידע שיש למערכת על קובץ stat --format=%A filename בשביל הרשאות בלבד stat --format=%F filename בשביל סוג קובץ בלבד stat --format=%U filename בשביל שם הבעלים של קובץ בלבד stat --format=%G filename בשביל הקבוצה של הבעלים בלבד stat --format=%U filename מאמין שיש עוד הרבה סינונים בפורמט הזה! לשאול את המאן פייג'

xdg-open

  • מריץ קובץ באמצעות ה-default program xdg open filename

xdg-mime

  • כדי לשנות את התוכנה שמריצה סוג מסוים של קבצים (כברירת מחדל)

To change the default app for a specific file type using xdg-mime, you can follow these steps: 1. Check the current default application for a file type: xdg-mime query default mime/type 2. Set the default application for a specific file type (e.g., setting VLC as the default for .mp4 files): xdg-mime default vlc.desktop video/mp4 Replace vlc.desktop with the .desktop file of your preferred application, and video/mp4 with the MIME type of the file. 3. Find the MIME type of a file (if you're unsure): xdg-mime query filetype filename This command associates the file type with the selected application, and will update your system's default app settings accordingly. 4. Find the current default program for a MIME type: Use the MIME-type pattern like so: xdg-mime query default mime/type xdg-mime query default application/pdf

  • כדי שנוכל להגדיר executable כלשהו כתוכנת default לסוג של קבצים, צריך [[לינוקס - כללי#.Desktop files|ליצור קובץ Desktop עבורו]]

להריץ קובץ

  • כדי לפתוח קובץ צריך כלי מתאים. program-name filename
  • הכי טוב עם xdg-open בתכלס
  • עוד אפשרויות: ב-WSL, הכי טוב explorer.exe filename כדי לתת לווינדוז לפתוח עם ברירת המחדל שלו

גיבוי, ארכוב, הצפנה

gzip, gunzip

  • שתי תוכנות טרמינל שמשמשות כדי לכווץ קבצים לפורמט .gz וכדי לחלץ קבצי gz gzip pathtofile כדי לארכב gunzip pathoffile כדי לחלץ
  • לשים לב לנואנס: אם לקובץ שלנו קראו file.txt, הוא יהפוך ל-file.txt.gz

    • וכעושים gunzip, הוא מניח כבר לבד את ה-.gz בסוף. אז כדי לחלץ את הארכיון file.txt.gz אפשר פשוט לכתוב gunzip file.txt אם רוצים. ![[Pasted image 20250127020313.png|500]]
  • אפשר להשתמש בפייפ וב-redirection ביחד עם gzip כדי לקחת אאוטפוט של פקודה ולארכב אותו:

  • ls -l /etc | gzip > foo.txt.gz
  • זה יצור ארכיב בשם foo.txt.gz עם כל המידע מ-ls -l על /etc (האם אנחנו נקבל את המבנה תיקיות הזה? או מה מאורכב? לבדוק)

    • לאחר בדיקה: זה די משעמם... זה יוצר קובץ טקסט עבור האאוטפוט של ls -l /etc ושם אותו בארכיב בשם foo.txt.gz...
  • כדי פשוט לקרוא את התוכן של ארכיון, אפשר להשתמש ב- gunzip -c pathtofile כדי לקבל את התוכן באאוטפוט, שאפשר גם לנתב בפייפ ל-less בשביל הנוחות

  • יש גם תוכנה בשם zcat שעושה אותו דבר כמו gunzip -c
  • יש גם תוכנה בשם zless שעושה אותו דבר כמו השתיים הקודמות ומראש מנתבת את האאוטפוט ל-less
  • לשים לב שאנחנו מכווצים קובץ בודד ל-gz ולא יוצרים ארכיון! ארכיון זה משהו אחר!

bzip2

  • עוד תוכנת קומפרשן, שיוצרת קבצי .bz2
  • עובדת כמעט זהה ל-[[מילון BASH#gzip, gunzip#|gzip]]
  • באה עם bunzip2 ועם bzcat לחילוץ הארכיונים
  • רק -number עובד שונה מב-gzip (לקרוא אם רלוונטי)
  • יש גם bzip2recover שיכולה לנסות לתקן ארכיונים פגומים
  • שוב, מדובר בכיווץ של קובץ בודד ולא ביצירת ארכיון

tar

  • כלי UNIX ליצירת ארכיונים
  • הפורמט שלהם יהיה .tar עבור סתם ארכיב ו-.tgz עבור ארכיון שכיווצנו עם gzip
  • ארכיון כזה כולל רשימת קבצים ו/או היררכיה של תיקיות (כמו zip או rar) tar mode[options] pathname pathname2...
  • לשים לב שנכתוב את ה-options ואת המוד ברצף, בלי -, ותמיד נכתוב את המוד לפני האפשרויות tar cf playground.tar ~/playground אומר שהוא רץ במוד c - יוצר ארכיב; עם האפשרות f - "אנחנו רוצים לכתוב את שם הארכיון שניצור"; עם שם הארכיון playground.tar עבור f; ואז עם ה-path לתיקיה שמארכבים, הפעם ~/playground.
  • הארכיון נוצר ב-working dir
  • האפשרויות למוד (עוד ב-man page) ![[Pasted image 20250127023809.png]]

לחלץ קובץ מסוים: tar xf archive-name-or-path path-in-archive path-in-arhive2... כדי לחלץ קובץ מסוים או כמה קבצים

  • בגרסת ה-GNU של tar, שהיא הגרסה הרווחת, אפשר להשתמש ב---wildcards כדי לאפשר שימוש בגלובים כשמגדירים את הקובץ/קבצים לחילוץ

שווה לשים לב: - ההרשאות של ארכיון שחולץ יהיו לפי היוזר שחילץ, ולא לפי הקבצים המקוריים - ה-pathים של tar הם תמיד רלטיביים ולא אבסולוטיים. - ~/playground נניח שארכבנו את הפאת' הזה - /home/user/playground זה מתרחב לפאת' הזה - אם נחלץ את הארכיון, הוא בעצם יצטרך להחזיר את הקבצים ל-folder structure של הארכיון... - אז מה שהוא יעשה זה לקרוא את הפאת' בלי הסלאש בהתחלה: home/user/playground - ובעצם להתחיל את ה-path מהמיקום בו הארכיון מחולץ. לצורך העניין יהיה לנו - ~/playground/home/user/playground - זה כמובן שימושי והכרחי, כדי שנוכל לחלץ folder structures בכל מקום במחשב... - בקיצור לזכור שהחילוץ רלטיבי ל-working directory שלנו (זה בתכלס לא שונה מזיפ וראר)

find playground -name 'file-A' -exec tar rf playground.tar '{}' '+' דוגמה לשימוש בטאר כפונקציה של פיינד, מציאת קבצים לפי כללים מסוימים וארכוב שלהם

find playground -name 'file-A' | tar cf - --files-from=- | gzip > playground.tgz אפשר גם באמצעות פייפ, כלומר טאר פועל עם סטנדרט אאוטפוט ואינפוט

  • אם מסמנים - בפאת' של tar עבור ה-filename (כמו שעשינו בדוגמה), זה אומר לו לכתוב את האאוטפוט ל-STDOUT במקום לקובץ (ארכיון)!
  • האפשרות --files-from שגם בשימוש בדוגמה אומרת לו לקרוא רשימה של פאת'ים מקובץ במקום לחפש בפייל סיסטם בעצמו. אפשר גם פשוט -T.
  • גם ב-Path של ה-files from השתמשנו ב--, הפעם לא כדי לנתב את האאוטפוט הבא אלא כדי לקלוט את האאוטפוט של הפייפ הראשון כאינפוט
  • בעצם אמרנו לטרמינל: "תכין קובץ של כל הפאת'ים שאתה מקבל מ-find; תשלח אותו לאאוטפוט; תכין קובץ ארכיון ותשים בו את כל הפאת'ים מ-(אאוטפוט של הפייפ הקודם), ואת הקובץ הזה תשלח לאאוטפוט; ואז תן את האאוטפוט הזה ל-gzip כאינפוט כדי שיכין מהארכיב קובץ tgz
  • הפורמטים tgz ו-.tar.gz הם אותו דבר

  • אפשר גם לתת ל-tar את האפשרות z כדי ליצור gzip אוטומטית, ואת j כדי ליצור bzip2 אוטומטית tar czf archivename או tar cjf archivename (דוגמה שכוללת גם מוד c וגם אפשרות f)

  • --checkpoint=100 → Show progress every 100 files

  • --checkpoint-action=dot → Prints a dot (.) per checkpoint

עם SSH ssh remote-sys 'tar cf - Documents' | tar xf - דוגמה לשימוש בטאר עם SSH! אמרנו בעצם: "תתחבר לרמוט, תשלח לו את הפקודה לקחת את כל התיקיה Documents ולהכין ממנה קובץ tar שהולך לאאוטפוט במקום לקובץ; את האאוטפוט הזה תיקח ל-tar במכונה המקומית, ותשתמש בו כדי לחלץ אותה ב-working directory" - לשים לב שצריך להשתמש גם בפייפ וגם ב-- בטאר 'המקומי'. זה בגלל שאנחנו גם מעבירים לו את האאוטפוט עצמו, וגם אומרים לטאר "תשתמש באאוטפוט" (אחרת הוא לאו דווקא מחפש להשתמש ב-STDOUT)

zip

  • תוכנה שמוכרת לנו מווינדוז, היא בעצם גם מכווצת וגם מארכבת באותה פעולה
  • פחות פופולרית מ-gzip ומ-tar בלינוקס zip -r archivename pathtoarchive כמו rm, בלי -r בשביל 'רקורסיביות', נארכב רק את התיקיה בלי התוכן שלה

  • לא חייב לכתוב .zip בשם הארכיב

  • חשוב לדעת שבניגוד ל-tar, אם נגיד ל-zip ליצור ארכיון שכבר קיים, הוא יעדכן את הקבצים בו בלבד ולא יחליף אותו unzip pathtozip כדי לחלץ

unzip -l [pathtofile] כדי להדפיס רשימה של קבצים, או רשימה של קבצים שמתאימים לפאת' כלשהו unzip pathtozip pathwithinzip כדי לחלץ קובץ ספציפי

  • אם החילוץ מתנגש עם קובץ קיים, נקבל פרומפט שישאל אותנו אם להחליף

  • אפשר לעשות פייפ לאאופוטים כדי שיהיה האינפוט של zip, וככה למשל ליצור ארכיבים לפי תוצאות של find find [arguments] | zip -@ archive-name ייצור ארכייב בהתאם ל-find. הדגל -@ הוא המקביל של - ב-tar, והוא אומר ל-zip תשתמש באאוטפוט הסטנדרטי כ-path שממנו אתה יוצר את הארכייב

  • לצערנו התוכנה unzip לא מקבלת STDIN, ולכן אי אפשר לחלץ מארכיב לפי תוצאות של פקודה אחרת...

  • התוכנה zip יודעת לקבל STDOUT ולכן אפשר לעשות ארכיב של אאוטפוט של פקודה, כמו עם טאר. נשתמש דווקא ב-- כמו ב-tar כדי להגיד לה להסתכל על האאוטפוט: ls -l /etc | zip archive-name -

  • התוכנה unzip יודעת לשלוח את הפלט שלה ל-STDOUT אם מוסיפים את הדגל -p unzip -p archive-name | less כדי לקבל רשימה של ארכייב מסוים ולפתוח עם לס

  • זיפ ואנזיפ משמשים בלינוקס בעיקר כדי לתקשר עם ווינדוז, אבל יש להם עוד הרבה אפשרויות ב-man page

rsync

  • תוכנת טרמינל שהיא הגרסה הפושטית של Rclone
  • משמשת כדי לסנכרן בין פאת' מקומי, רמוט פאת' או שרת rsync ככה שהתיקיות יהיו mirrored, ויודע לעשות את זה במינימום פעולות ההעתקה הנדרשות (יעיל) rsync options source-path destination-path לפחות צד אחד של הסנכרון (source או destination) חייבים להיות מקומיים - אין סנכרון בין remoteים.

--a בשביל לשמר file attributes --v verbose output בשביל -delete כדי למחוק מהיעד קבצים שבר לא קיימים במקור

כותבים /source או /desination (עם / בסוף) כדי לא להעתיק את התיקיה, אלא רק את התוכן שלה (מתחילים את הפאת' אחרי התיקיה ולא על התיקיה)

  • ה-R עומדת בשביל REMOTE! אפשר להשתמש ב-rsync כדי לסנכרן קבצים מרמוטים
  • --rsh=ssh כדי להגיד לארסינק שהוא מתחבר באמצעות SSH
  • remote-name:path-in-remote נציין לארסינק את השם של הרמוט ביחד עם הפ'את' ברמוט, כמו שעושים עם SSH

  • עוד אפשרות היא להשתמש ב-Rsync Server! בעצם, ל-rsync יש אפשרות לרוץ כ-Daemon שנועד פשוט לקבל בקשות סנכרון.

  • זה שימושי כשיש תיקיה שהרבה אנשים צריכים לסנכרן ממנה לעתים קרובות. למשל, כל החבילות הנסיוניות של פדורה יושבות על Rsync Servers שמוחזקים בכל מיני אוניברסיטאות. בטא טסטרים עושים sync עם התיקיות האלה כדי לקבל את כל החבילות הנסיוניות העדכניות ליום ספציפי.
  • נכתוב את ה-source path באופן שמרפרר לשרת: rsync://archive.linux.duke.edu/fedora/linux/development/rawhide/Everything/x86_64/os/

המבנה הוא פרוטוקול (rsync://), שם הרמוט (archive.linux.duke.edu) והפאת' לרפוזיטורי הרלוונטי בתוך הרמוט (/fedora/linux/development/rawhide/Everything/x86_64/os/)

gpg

  • חבילה של GNU, קיצור ל-GNU Privacy Guard
  • נועדה כדי להצפין קבצים! gpg -c pathtofile כדי לייצר קובץ מוצפן שלו. נתבקש להזין ססמה לקובץ. הקובץ ייקרא filename.gpg ואי אפשר לפתוח אותו בלי לעשותו לו decryption

    `gpg pathtofile`
    

    כדי לפתוח את ההצפנה. נתבקש להכניס ססמה, ו-GPG ייצור אוטומטית קובץ חדש שנקרא באותו שם, רק בלי הסיומת GPG. אם הקובץ כבר קיים (המקור באותה תיקיה), הוא ישאל אם לעשות override או לבחור שם חדש. gpg -o pathtofile destination/path/filename כדי להגדיר מראש אאוטפוט

  • כדי לפתוח את הקובץ: gpg -d pathtofile > pathto-destination-file לא לשכוח את ה-redirection! אחרת סתם נקבל את ה-data של הקובץ כאותוית בטרמינל...

  • כדאי לשים לב - כשיוצרים הצפנה, GPG יוצר כל מיני קבצים בשם .gnupg בתיקיית הבית שלנו. הם בעצם מכילים את הססמאות שהגדרנו, וככה אפשר לפתוח הצפנות בלי להזין ססמה. במערכת בלי הקבצים האלה - נתבקש להזין. אם אנחנו רוצים להעיף את השמירה הזו מהמערכת שלנו - למחוק את הקבצים האלה rm -r ~/.gnupg*

  • אי אפשר להגדיר את הססמה מראש כ-Argument, אבל אפשר ככה: echo "your_password" | gpg --batch --yes --passphrase-fd 0 -c /path/to/file

    • echo "your_password": This sends your passphrase to the standard output.
    • --batch: Tells GPG to run in batch mode, which is useful for automation and prevents interactive prompts.
    • --yes: Automatically answers "yes" to any prompts (e.g., for overwriting files).
    • --passphrase-fd 0: Specifies that the passphrase should be read from file descriptor 0 (stdin, which is where the echo command sends it).

חיפוש

כמעט כל הפקודות כאן הן חלק מחבילת [[לינוקס - כללי#GNU#|GNU]] בשם findutils

locate

  • תוכנת טרמינל פשוטה שמחפשת את כל ה-pathים שמתאימים ל-string כלשהו
  • זה אומר שאפשר לחפש שם של קובץ ככה
  • locate filename
  • וזה בגלל ששם הקובץ הוא תמיד הקצה של ה-pathname של הקובץ
  • אפשר לחפש גם path מורכב יותר, נניח:
  • locate bin/zip
  • יחפש את הקובץ zip רק ב-pathים שמתחילים בתיקיה bin (אנחנו יכולים להניח שתוכנה תשב בתיקיית bin, וככה צמצמנו את מס' ה-pathים ש-locate צריך לסרוק)
  • לשים לב שכל path שמתחיל ב-query שלנו גם תופס - למשל הפקודה הקודמת תמצא גם את /bin/ziplock ואת /bin/zipzip

  • אפשר להשתמש ב-regexp מסוג bre באמצעות --regexp וב- ב-regex מסוג ere באמצעות --regex BRE & ERE

  • לוקייט היא בעצם שתי תוכנות, mlocate ו-plocate, שהן דומות אבל יש ביניהן קצת הבדלים. לכל מערכת יש אחת אחרת, ובד"כ הן מחוברות ב-symlink לפקודה locate

  • אם אנחנו רוצים לדעת אם יש לנו תמיכה ב-regex וב-globs, צריך לקרוא את ה-man של הגרסה שלנו.

  • הפקודה locate מחפשת ב-database של pathים שנוצר ע"י תוכנה אחרת בשם updatedb. לרוב המערכות יש cron job של פעם ביום להריץ את updatedb.

  • אם אנחנו רוצים לעדכן את ה-database לפני ה-cronjob היומי, נעשה sudo updatedb

find

  • תוכנת טרמינל לחיפוש קבצים שהיא קצת יותר מורכבת מ-locate
  • האופרציה הבסיסית ביותר שלה פשוט נותנת רשימת קבצים ב-path, למשל ככה:
  • find ~ פשוט ירשום את כל הקבצים שיש בהום

  • בשלב הבא, find יודעת לסנן את רשימת הקבצים לפי קריטריונים שונים, שמתחלקים ל-options, tests ו-actions

TESTS

find path -type x בשביל להגדיר סוג של קובץ שאנחנו רוצים לסנן לפיו. למשל find ~ -type d כדי לקבל רשימה של כל התיקיות ב-home ![[Pasted image 20250127005741.png]]

find path -name "query" כדי לסנן את הרשימה לפי שם של קובץ. נניח find ~ -name "*.jpg" j כדי למצוא את כל הקבצים שמסתיימים ב-.jpg ב-home. (אפשר להשתמש בגלובים) find path -size +/-/nullSIZE

כדי לסנן את הקבצים לפי גודל. אפשר לעשות + בשביל קבצים גדולים מהערך, - בשביל קבצים קטנים מהערך, או לא להשתמש בשום אופרטור (null) בשביל למצוא קבצים ששווים לערך בדיוק. למשל find ~ -size +20M בשביל קבצים ב-home שגדולים מ-20 מגה.

![[Pasted image 20250127010203.png]]

רשימה חלקית של טסטים נוספים (עוד ב-man) ![[Pasted image 20250127010317.png]]

OPERATORS

  • יש אפשרות לחבר טסטים שונים של find באמצעות אופרטורים לוגיים.
  • כדי לחבר טסטים, נחבר אותם באמצעות operator, ככה למשל אפשר לכתוב:
  • -type f -not -perm 0600 כדי למצוא את כל הקבצים הרגילים שהם לא בהרשאה 0600 (אוקטאלי)
  • אם רוצים לחבר כמה ביטויים בסוגריים, הסינטקס קצת מוזר. נניח שבנוסף לסינון לעיל, אנחנו רוצים לחבר בפונקציה של OR את הסינון:
  • type -d -not -perm 0700 כדי למצוא גם תיקיות שההרשאה שלהן היא לא 007 אוקטאלי
  • נחבר את הסוגריים ב-OR באופן הבא:
  • find path \( -type f -not - perm 0600 \) -or \( -type d -not -perm 0700 \)
  • זה סינטקס קצת מוזר, בהתחלה בקסלאש לפני הסוגריים ואז הפוך בסוף. ועם רווחים לפני הטקסט שבסוגריים ולפני הבקסלאש שבסופן...
  • לשים לב שלא היינו צריכים לכתוב and- לפני not- בסוגריים. זה בגלל **ש-find מניח מראש את ה-and כשאנחנו מוסיפים אופרטור לשרשרת
  • מה שעשינו לסוגריים עם הבקסלאש זה בעצם להקדים כל סוגר בבקסלאש. המטרה של זה היא, כרגיל, [[מילון BASH#backslash#|להיות escape character]]. לסוגריים יש משמעות בטרמינל, ואנחנו רוצים ש-find יקבל אותן באינפוט בלי שישתנו (expansion), כי הוא יודע לעשות להן Parsing משל עצמו. לכן אנחנו כותבים
  • \( -test value -operator -test value \) (עדיין עם רווח לפני הפקודה...)

![[Pasted image 20250127011342.png]]

  • כדאי לשים לב שאם כתבנו
  • expr1 -operator expr2
  • אז expr2 לא בהכרח יתבצע, אלא רק בהתאם לשער הלוגי של האופרטור:
    • לצורך העניין, אם אנחנו מחפשים קבצים שהם או a או b, ועבור קובץ מסוים, find זיהה שהוא a, יש לנו a=true ולכן הוא לא יבדוק האם b (expr2) הוא true עבור הקובץ או לא. ![[Pasted image 20250127012135.png|500]]

ACTIONS

  • ל-find יש מבחר של פעולות מוגדרות מראש שניתן לבצע על רשימת הקבצים שהתקבלה. ![[Pasted image 20250127012313.png|500]]

  • כלומר, אם לא כתבנו פעולה, find מניח -print

  • כדי לכתוב פעולה אנחנו כותבים אותה כדגל: find path -test value -operator -test value -ACTION
  • כמו בין test queries, גם בין כל טסט/פעולה ובין כל פעולה, התוכנה מניחה -and
  • כלומר, שאם כתבנו את הפעולה בסוף הפקודה כמו למעלה, היא תרוץ רק אם כל הקוניונקציה של האאוטפוט מחזירה TRUE (כל מה שמחובר ב-AND- בלתי נראים )
  • find path -ACTION -test value -operator -test value ואם נכתוב בהתחלה אז ה-ACTION תרוץ ראשונה, ובלי הכרח שכל החוליות הקודמות בקוניונקציה יחזירו T

-exec command 'currentpath' ';' כדי להריץ כל פקודה שיש לנו בטרמינל על הרשימה. את הפקודה כותבים אחרי -exec את ה-path ואת ה-semicolon אנחנו עוטפים בגרשיים כי יש להם משמעות בטרמינל, שאנחנו לא רוצים שתתרחב לפני שהם מגיעים ל-find כאינפוט. - לא הבנתי איזה pathname כותבים. בדוגמה בספר זה - -exec rm '{}' ';'

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

  • לפעמים אנחנו לא רוצים להריץ את ה-ACTION על כל תוצאה של החיפוש בנפרד, אלא בעצם "לאגור" רשימה של תוצאות החיפוש ולתת את הרשימה כולה כארגומנט להרצה אחת בלבד של הפקודה.

  • אפשר לעשות את זה ע"י החלפת ה-';' ב-+ (בלי גרשיים), כך שהפקודה תיראה (למשל) ככה:
  • find ~ -type f -name 'foo*' -exec ls -l '{}' + הפקודה הזו אומרת למצוא את כל הקבצים הרגילים שמתחילים ב-foo, ואז ברגע שיש לנו את הרשימה של כולם, להריץ על כל הרשימה ביחד ls -l

OPTIONS

  • האפשרויות קובעות את ה-scope של החיפוש, משתמשים בהן כדגלים באינפוט בדומה לטסטים ופעולות. הן לא צריכות לקבל ערך. ![[Pasted image 20250127015409.png|500]]

xargs

  • פקודה מעניינת שמקבלת אינפוט מ-standard input (או הקלדה בטרמינל, או מהקצה המקבל של [[מילון BASH# pipe#|pipe]]), והופכת אותו לרשימת ארגומנטים עבור פקודה (שתבוא אחריו בסינטקס) command | xargs command2 כדי להפוך את כל האאוטפוט של command לארגומנט של command2. דוגמה קונקרטית: find ~ -type f -name 'foo*' -print | xargs ls-l כדי למצוא את כל הקבצים הרגילים שמתחילים ב-foo, להדפיס רשימה שלהם, ואת הרשימה הזו לתת ל-ls -l כארגומנט (בשביל שידפיס את הרשימה מחדש בפורמט שלו).

  • לטרמינל יש הגבלה של אגרומנטים, אם ניתן לפקודה xarg ארוך מדי הוא פשוט יריץ את המס' המקסימלי האפשרי. אפשר להריץ את xarg עם הדגל הבא כדי שיראה את הלימיט: xargs --show-limits

null separation

  • כשאנחנו משתמשים ב-find, או ב-xarg, יש כל מיני תווים מיוחדים בשמות של קבצים שיכולים לזיין לנו את הרשימה ולהכניס ברייקים איפה שאין ברייקים.
  • לכן, לשתי הפקודות הללו יש אפשרות להריץ ככה שרק ה-null character (ASCII #0) תחשב ל-argument separator (רק היא מסמלת שעברנו לארגומנט הבא).
  • -print 0 בפקודת find
  • --null or -0 בפקודת xargs

  • לשים לב ש-null זה לא ספייס! צריך למצוא את התו הבלתי נראה שהמס' שלו ב-ASCII הוא 0

  • לא צריך באמת לשים Null בשמות הקבצים שלנו! אנחנו משתמשים באחת מהאפשרויות האלה כדי להתעלם מכל סוג של ריווח או שבירת שורה בשמות הקבצים שאנחנו פועלים עליהם. בין קובץ לקובץ תמיד יהיה מראש null (בלי קשר למה שיש או אין באמצע, בתוך השמות של הקבצים).

regular expressions (regexp)

הקדמה

  • זה תיעוד מקורי שלי (קצת מבלבל לפעמים) של Regexp.
  • הסברים ברורים יותר לקרוא כשכותבים, אפשר למצוא ב-Regexp
  • כלים מסורתיים של GNU עובדים עם bre syntax, ואם נותנים להם e- אז עם ere syntax דוגמאות בולטות: sed, grep

  • בגדול regex דומה לגלובים... זו דרך להגיד לכלים שונים בטרמינל לזהות תבניות בטקסט סטרינגס

  • יש כל מיני אימפלמנטציות מעט שונות של regex אנחנו נתמקד בגרסה המפורטת ב-POSIX STANDARD, שהיא הרווחת בטרמינל

  • כל תו רגיל שנשים ב-string שלנו הוא literal character זה אומר שהתו מסמל את עצמו ושה-regular expression מתאים את הסטרינג לכל סטרינג עם אותם Literal characters שמסודרים באותו הסדר

    חוץ מ-literal characters, יש לנו ב-regex גם metacharacters שמשנים את ההגדרה של "התאמה" ^ $ . [ ] { } - ? * + ( ) | \ המטא-תווים לשים לב ש- \ backslash מיועד להפוך תו מטא ל-Escaped character - לשים לב גם שרוב המטא-תווים הם בעלי משמעות 'מתרחבת' בטרמינל, לכן חייב לעטוף אותם בגרשיים (את כל הסטרינג) כשאנחנו נותנים אותם כאינפוט

  • סוגי מטא-תווים והשימוש בהם:

* & . - ANY character, any NUMBER of times

. (נקודה): - עובדת כמו הגלוב * בבאש הרגיל (גלובים) עם הבדל חשוב: - בעוד ש-* בבאש מוצאת תו שרירותי אחד או יותר - נקודה . ב-Regex מוצאת תו שרירותי אחד בלבד (כולל רווח או שבירת שורה) - כדי להגיד "תו שרירותי, מס' שרירותי של פעמים" יש לשלב עם * * (כוכבית): - בתכלס זה quantifier (כמתים ב- regex) - אבל נכתוב עליו בנפרד כי מאוד נפוץ להשתמש בהם יחד... - * אומר "מה שבא לפניי, כל מס' של פעמים" - אז לעשות .* (נקודה ואז כוכבית) זה המקביל של Regex ל-* בבאש...

- אם נכתוב ב-regex את הסטרינג  `zip*.` - זה יעבוד כמו הגלוב המקביל; אנחנו נמצא כל פריט שיש לו **לפחות 4 תווים,** כאשר השלושה האחרונים הם `zip`. 
- לשים לב ש-`.` מתרחבת **לתו אחד לפחות** - וביחד עם כוכבית, **לתו אחד או יותר**
    בדוגמה מעל, נמצא גם את `azip` וגם את `abzip`, אבל לא נמצא את `zip`, כי אין לו שום תו שמהווה את `.`.

^ and $ - Anchors

^string ימצא לנו רק תוצאות שמתחילות בסטרינג string$ יתן לנו רק תוצאות שמסתיימות בסטרינג ^string$ יתן רק תוצאות שמתחילות ונגמרות בסטרינג ^$ יתאים רק ל-blank lines

[] Bracket Expressions

grep -h '[bg]zip' יחפש לנו קבצים שנקראים או bzip או gzip

  • אפשר לכלול metacharacters בתוך הסוגריים והם יחשבו חלק מהדיסיונקציה, כתו רגיל
  • יוצאי הדופן הם ^ שמסמל שלילה, ו-- שמגדיר טווחים
  • grep -h [^bg]zip ימצא את כל מה שמסתיים בזיפ ולא מתחיל בבי או ג'י (כלומר, כל מה שבא אחרי ^ הוא שלול, לא צריך לשלול כל אות) לשים לב שאות שלולה עדיין מחייבת אות כלשהי (אחרת) - החיפוש הזה לא ימצא את zip
  • grep -h [a-z]zip ימצא כל קובץ שהשם שלו הוא אות בטווח שצוין ואחרי זה זיפ grep -h ^[A-za-z0-9] אפשר לחבר כמה טווחים, בלי רווח, נגיד כאן נמצא את כל מה שמתחיל באות גדולה או קטנה, או במספר

  • אם רוצים ש-- יהיה חלק רגיל באקספרשן, פשוט לא נשים אותו באמצע, אלא בהתחלה... grep -h [-AZ] כדי לחפש או דאש, או איי גדולה או זי גדולה

Character Classes

הכתיבה של טווח אותיות לא תמיד עובדת באופן צפוי, בגלל סיבות היסטוריות. כשלינוקס יצא הוא הניח שיש רק ASCII: איפה שתווים 0-31 הם control codes ו-32-63 הם אותיות שניתן להדפיס (כולל פיסוק ומספרים); 64-95 הם אותיות גדולות ועוד כמה סמלים; 96-126 הם אותיות קטנות ועוד כמה סמלים. מערכות שיש להן רק ASCII מסדרות את התווים ככה: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

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

עם הפופולריזציה של UNIX בעולם, נדרשו עוד תווים כדי לכתוב בעוד שפות; לכן ה-ASCII הורחב ל-8ביט ונוספו לו התווים 128-255, שכללו אותיות עבור שפה נוספת. נוצר המושג של locale, שהוא character set של 0-255 שמותאם לשפה מסוימת. ה-locale מוגדר דווקא ע"י קובץ מילון - אז אם כתבנו ב-regex את הטווח [A-Z] ואנחנו מול קובץ מילון, נקבל טווח מוזר של כל האותיות חוץ מ-a קטנה... כדי להתגבר על הפאקרי הזה, יש ב-POSIX כמה סטנדרטים לכתיבה של טווחים, שנקראים Character Classes

![[Pasted image 20250127155750.png|500]]

  • אפשר לשנות את ה-locale במערכת ל-posix כדי לחזור להתוויה המקורית אם רוצים. [[לינוקס - כללי#LANG#|עוד על לשנות את ה-locale בהסבר הזה]]

BRE & ERE

  • כאן אנחנו נכנסים לפאקרי האמיתי: POSIX מחלק את ה-regex לשני סוגים: basic regular expression (BRE) ו-extended regular expression (ERE).
  • כל תוכנה בסטנדרט של POSIX תומכת ב-BRE, אבל רק חלקן תומכות ב-ERE.
  • ב-BRE, התווים הבאים בלבד הם מיוחדים (מטא): ^ $ . [ ] *
  • בעוד שב-ERE, התווים הבאים מתווספים למטא: ( ) { } ? + |

  • הקאצ' הוא שב-BRE, שני סוגי הסוגריים (), {} דווקא כן יהיו מיוחדים/מטא אם ניתן להם backslash

  • זאת בעוד שב-ERE, באקסלאש לפני כל תו הופך אותו למילולי (מודפס כעצמו)

| Alteration

  • שייך ל-ERE, דורש grep -e או egrep
  • כמו ש[xy] ב-bre regex אומר "או x או y", לעשות grep -E 'AAA|BBB|CCC' ב-ere regex אומר "או הסטרינג AAA, או הסטרינג BBB, או הסטרינג CCC"
  • בקיצור אפשר להשתמש ב-| כדי ליצור שרשראות דיסיונקציה של סטרינגים עבור ה-regex query
  • אם רוצים לשלב שרשרת דיסיונקציה כזו עם עוד regex, שמים אותה בסוגריים: grep -E '^(AAA|BBB|CCC)' יתאים רק לפרטים שמתחילים באחד מהסטרינגים

^ Negation

  • בשני סוגי ה-regex, אם אנחנו רוצים את המקביל ה-regexי של אופרטור NOT, אנחנו משתמשים ב- [^query] כלומר, גם בסוגריים מרובעים וגם בסימן ^, חשוב לזכור שמחוץ לסוגריים מרובעים המשמעות שלה שונה - הוא אומר "בתחילת השורה"

  • יש אפשרויות יותר יעילות ל-queryים מסובכים: הכי פשוט זה לחפש באמצעות grep -v, הדגל v הוא בדיוק "הפיכה" של החיפוש מהתאמות לאי-התאמות

    אפשרות יותר מתקדמת היא להשתמש ב-awk באופן הבא: awk '!/query/' file.txt הסימן ! ב-awk גם הוא מסמל "NOT"

Quantifiers

?

  • שייך ל-ERE, דורש grep -e או egrep
  • הסימן ? אומר ב-ere: "מה שקדם ל-?, יכול להיות מותאם או 0 פעמים או פעם 1"
  • בוא נניח שאנחנו רוצים לקבל מס' טלפון, והוא יכול להיות או nnn-nnnn או (nnn) nnn-nnnn (קידומת אפשרית בסוגריים)
  • כדי לכתוב regex query שיחזיר התאמה בשני המקרים, נכתוב ככה: ^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$
  • הסבר: אנחנו מתחילים מ-^ כדי לחפש כל סטרינג שמתחיל בפורמט הנכון (לא חובה...); אנחנו משתמשים ב-\ כדי לכתוב סוגריים בלי שתהיה להם משמעות, ובתוך הסוגריים כותבים שלוש פעמים את הטווח [0-9] כדי להגיד "כל שלוש ספרות בתוך סוגריים"; כל העסק עטוף גם ב-? כדי להדגיש שלא חייב להיות חלק כזה ב-string בכלל.

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

* - הסימן * אומר משהו אחר מב-BRE REGEX. הוא דומה ל-?, אבל מתאים גם עבור 0 הופעות, גם עבור 1 וגם עבור יותר מ-1 - כלומר במקום להגיד "מה שלפני ? יכול לחזור פעם אחת או שבכלל לא", אנחנו אומרים "מה שלפני * יכול לחזור בין 0 לאינסוף פעמים" (וזו תישאר התאמה)

[[:upper:]][[:upper:][:lower:] ]*\. זה query יפהפה שמחזיר התאמה רק למשפטים שבנויים נכון גרמטית: מתחילים באות גדולה, כוללים איזשהו מיקס של אותיות גדולות וקטנות, ומסתיימים בנקודה. - אנחנו מסמלים ב-^ "תתאים למה שמתחיל ככה"'; מכניסים רצף של שלושה טווחים לפי הסטנדרטיזציה של POSIX - מתחילים בטווח "אות גדולה" וממשיכים לדיסיונקציה (תחומה בסוגריים מרובעות) של הטווח "אות גדולה" או "אות קטנה"; הסיקוונס הזה מקבל בסופו * כדי להגיד שהוא חוזר בין 0 לאינסוף פעמים, ולאחר שהוא חוזר x פעמים אנחנו מצפים לנקודה, שמקדים אותה באקסלאש כדי שתתפרש כנקודה טקסטואלית. בעצם הגדרנו משפט כ-"סיקוונס שמתחיל באות גדולה, כולל עוד 0 עד אינסוף אותיות גדולות או קטנות ואז מסתיים בנקודה". +

  • הסימן + עובד דומה ל-? ול-*, אבל אומר "תתאים אם מה שלפני + מופיע לפחות פעם אחת" grep -E '^([[:alpha:]]+ ?)+$ יתאים לכל שרשרת של מילים אלפבתיות (עם אותיות בלבד) שתחומות בסוגריים ומופרדות ברווחים (quite like that)

{} - הסוגריים {} יכולות לשמש כ-quantifier גם הן, ולהגדיר בדיוק כמה מופעים של הסטרינג אנחנו רוצים כדי להחשיב את זה להתאמה. יש כמה פורמטים אפשריים: ![[Pasted image 20250127170017.png|500]]

^\(?[0-9]{3}\)? [0-9]{3}-[0-9]{4}$ עוד ניסוח לזיהוי של מספרי טלפון

subexpressions, backreference

שימושי במיוחד ל[[מחברות קוד/Bash&Linux 🐧/מילון BASH#sed#|sed]] - בתוכנות מסוימות שתומכות ב-bre, אפשר להשתמש ב-back reference.

אם הסיקוונס n/ מופיע בשדה ה-replacement, כאשר n הוא מספר בין 1-9, ההחלפה  תתבצע עבור ה-subexpression התואם **ב-regular expression הקודם** (במקרה של sed, בשדה ה-regexp)

`[0-9]{2}/[0-9]{2}/[0-9]{4}$` 
הוא ביטוי regex שילכוד תאריכים בפורמט MM/DD/YYYY 
מה אם אנחנו רוצים להחליף אותם בפרמוט אחר של אותו המידע? נעשה: 
`([0-9]{2})/([0-9]{2})/([0-9]{4})$`
כל חלק ב-regex שעטוף בסוגריים רגילות `()` ניתן לרפרור ב-regex הבא כ-subexpression 
`\3-\1-\2`
בשדה ההחלפה יבוא אותנו ל- `YYYY-MM-DD`
  • נחדד: מגדירים את ה-subexpressions באמצעות עטיפתם בסוגריים () ב-regex הראשון משתמשים ב-subexpressions ע"י כתיבה של n/ ב-regex הבא כאשר n הוא מס' ה-subexpression (ממוספרים לפי סדר הכתיבה)

נואנסים

  • חשוב לדעת ש-grep מתאימה ל-query כל פריט שמכיל אותו, בעוד ש-find מתאימה ל-query רק פריטים שתואמים לו בדיוק

    grep "^pattern" בשביל תוצאות שמתחילות במשהו מסוים

אינפוט/אאוטפוט וסינונים

exit status

  • לכל פקודה שרצה יש סטטוס יציאה שמשקף את ההצלחה שלה. הסטטוס הוא ערך בין 0 ל-255. exit status 0 = הצליחה exit status 1+ = נכשלה תוכנות מסוימות נותנות ערך 1 לכל כישלון; אחרות משתמשות בערך מדויק יותר בין 1 ל-255 כדי לאפיין את השגיאה.

  • זה מה שההתניות השונות משתמשות בו כדי להשתמש בפקודה כתנאי

  • אפשר לרפרר לסטטוס היציאה האחרון ע"י $? לולאת if למעשה אומרת "תריץ מה שבסוגריים, ואם בסוף $? שווה ל-0, תעשה את הפקודות הבאות:"

true, false

  • פקודות שפשוט אומרים לטרמינל שעכשיו $? (exit status) יהיה שווה ל-0 או ל-1 בהתאם
  • שימושי כדי ליצור שורה מסוימת בסקריפט, שאם היא רצה, היא מגדירה את ה-exit status באופן מסוים
  • במילים אחרות, לולאת if אומרת if true ו-`if false'

redirect operators

>, >> Output Redirection

מנתבים את הפלט של הפקודה שלפניהם למקור אחר. למשל קובץ. echo "Hello World" > output.txt הולך למחוק את output.txt ולכתוב אליו hello world. כלומר משכתב את הקובץ echo "Hello again" >> output.txt הולך להוסיף את Hello World בתחתית של הקובץ, מתחת לתוכן הקיים אם יש כזה. כלומר, מוסיף לקובץ. (מדובר באאוטפוט [[#אינפוט/אאוטפוט וסינונים#STDERR & STDOUT|STDERR]] בלבד - כלומר הפלט הסטנדרטי של פקודות)

< Input Redirection

מעביר פאת' מסוים להיות הקלט של פקודה (שנכתבת ראשונה, לפי כיוון החץ) למשל sort < input.txt יפעיל פקודת sort על הטקסט שב-input.txt

&>, 2>&1, 2> Redirecting Output & Errors

&> עובד כמו [[#>, >> Output Redirection|רידיירקט]] אבל מדפיס גם [[#אינפוט/אאוטפוט וסינונים#STDERR & STDOUT|STDERR וגם STDOUT]] 2>&1 הוא כמעט אותו דבר, אבל במקום לכוון את שני הסטרימים באותה פקודה, הוא פשוט אומר "2 הולך לאן ש-1 הולך" אפשר גם פשוט <2 או <<2 כדי להגיד "2 הולך ל-(פאת') (כתוספת או כשכתוב)", כמו שמוסבר ב- [[#אינפוט/אאוטפוט וסינונים#STDERR & STDOUT|STDERR וגם STDOUT]] כל הפקודות עובדים גם כ-> וגם כ-<

<(...), >(...) Angel Parenthesis\process substitution

  • שילוב של סוגריים עגולות (...) ושל רידירקציה עם ``

  • יש לנו את האפשרות לקחת ערך של subshell operation (כל מה שבסוגריים) ולנתב את האאוטפוט שלו, כ-file descriptor, כלומר כערוץ חדש של אינפוט/אאוטפוט: השימוש בזה מאפשר לנו להשתיל פקודות בתוך פקודות, בדומה לשימוש ב-pipe |, אבל גם במקומות שלא ניתן להפעיל בהם pipe - כמו בארגומנטים של פקודה:

    sort <(ls path) <(laspath2) <(lspath3) היא למעשה שקולה ל:
    sort "$(ls path)" "$(ls path2)" "$(ls path3)" אבל מאפשרת לנו לשלוט בערכי הפונקציות באמצעות 3 ערוצי פלט/קלט נפרדים.

  • לעתים נצטרך לעשות command < <(read), כאשר הפקודה לא לוקחת target file כארגומנט, אלא רק באמצעות רידיירקט (ה> של ה-angel parenthesis לא נחשב).

  • שימוש נפוץ הוא בשילוב עם read ועם לולאה כדי לתרגם אינפוטים למשתנים נפרדים בקלות:

    #!/bin/bash
    # pro-sub: demo of process substitution
    while read -r attr links owner group size date time filename; do
    cat << _EOF_
    Filename: $filename
    Size: $size
    Owner: $owner
    Group: $group
    Modified: $date $time
    Links: $links
    Attributes: $attr
    _EOF_
    done < <(ls -l --time-style="+%F %H:%m"| tail -n +2)
    
    לשים לב לשימוש המיוחד ב ()> כאשר אנחנו מזינים אותו ללולאה: הוא נרשם כאילו הוא מתייחס לפקודה done (בשורה האחרונה של הלולאה).

  • חשוב לזכור שה-File Descriptor הוא קובץ הקובץ הזה מקבל Path בסגנון של /dev/fd/63 אז עבור פקודה שמקבלת filepath, אפשר להשתמש ב-Process substitution, למשל cat אבל עבור פקודה כמו echo שמקבלת סטרינג - הביטוי פשוט יחזיר את הערך של הפאת'...

<<, <<- <<< Here Documents/Strings

<< Here Document:
  • מאפשר לנתב לפקודה אחת מספר שורות כאינפוט cat << TOKEN This is a multi-line input passed to cat. TOKEN The content between << EOF and EOF is passed to cat as input. אפשר לבחור כל סטרינג להיות ה-TOKEN (מחליט מתי נגמר האינפוט שנעביר לפקודה)

  • התרחבויות עובדות כרגיל, אבל אפשר לעטוף את ה-TOKEN במרכאות בודדות כדי לגרום לו שלא להרחיב $ וכו' (אחרת צריך Escape Backslash כדי לכתוב אותם): cat << 'TOKEN' Thi$-will-not-expan\d; _EOF_ הוא השם המקובל לטוקן אם אין צורך אחר

  • הוריאציה ->> עושה שה-Here doc יתעלם מאינדנטציה (רווחים שמקדימים שורה) אם הם נוצרו באמצעות tabs - אם spaces זה לא עובד (יש תוכנת המרה, לחפש במחברת)

<<< Here string:

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

  • מאפשר לנתב לפקודה אחת שורה אחת בלבד כאינפוט אפשר לעשות echo עם פייפ (|), אבל אפשר גם פשוט ככה: grep "pattern" <<< "This is a test string" The string "This is a test string" is passed as input to grep to search for "pattern". (לשים לב ש-path הוא לא string!) יתרון של השימוש ב-HERE DOCUMENT: הטרמינל מתעלם מגרשיים. אפשר לכתוב echo "hello" והטרמינל ידפיס "hello" כאילו הן חלק רגיל בסטרינג.

  • מאפשר לחבר פקודות בלי ליצור Subshell החיסרון המרכזי של פייפים | הוא שהם יוצרים subshell בכל פעם שאנחנו פותחים אחד (הפייפ בעצם פותח סשן חדש שמקבל כאינפוט את האאוטפוט של הסשן המקורי, וכך הלאה עם כל פייפ.)

    אם אנחנו עובדים עם פקודה שמגדירה משתנים עבור ה-shell session שלנו - למשל read, לעשות משהו כמו echo "option" | read יחזיר לנו blank בערך של $REPLY, וזה בגלל ש-read רצה ב-subshell משלה, וה-subshell לעולם לא יכולה להשפיע על ה-parent shell שלה. השורה read <<< option תעבוד ותחזיר לנו "option" ב-$REPLY

| pipe

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

command | command 2מנתב את הפלט של הרישה ישירות לקלט של הסיפה כך אנחנו עורכים סינון בתהליכים רצים למשל: ps aux | grep query

עוד דוגמה: `ls -l | grep ".txt" יגרום ל-grep לחפש קבצי טקסט בפלט של ls -l

filter

נהוג להשתמש בדאבל פייפ כדי ליצור 'פילטרציה' למשל ls /bin /usr/bin | sort | less יעשה לנו רשימות של שני הפאת'ים, יעשה להם סורט (הפילטר) ואז יתן את הפלט ל-less כדי שנוכל לקרוא אותו

mkfifo Named Pipe

  • הפקודה נועדה ליצור קבצי fifo, שהם בעצם קובץ שמתפקד כפייפ - כלומר, מקבל קלט מפקודות, ומייצא אותו כפלט עבור פקודות. אם פייפ מחייב אותנו לשים פקודות אחת אחרי השניה, משני הצדדים שלו, הקובץ מאפשר לנו לעשות command1 > pipe_name ואז בסקריפט או בטרמינל סשן אחר: command2 < pipe_name כאשר התוצאה תהיה כמו לעשות command1 | command2, רק בין תהליכים נפרדים: כלומר, ברגע ש-command1 תרוץ, command2 תרוץ מיד כאילו קיבלה את הפלט של 1 בפייפ. > האם command2 תרוץ ככה ברגע שהיא תרוץ, לפי הסדר של הסקריפט שלה, או מידית כש-1 רצה?

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

  • הסיבה שהוא נקרא fifo היא שהקובץ הזה הוא למעשה text buffer, שיודע איפה לשבץ אינפוטים חדשים, ואילו אאוטפוטים לייצא בכל פעם, לפי היגיון של first in - first out.

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

  • יצירה של קובץ fifo: mkfifo <pipename> הקובץ אכן נוצר במערכת שלנו - אם נעשה לו ls -l נוכל לראות p בהתחלה של סטרינג ההרשאות שלו - זה מסמן שהוא named pipe.

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

flags

בגדול שני הסוגים נקראים flag והם פשוט נועדו לסמן אפשרויות, אין פה חוקים מורכבים. השורה השלישית מעניינת: אם רוצים לגעת בקובץ שמתחיל ב'-', משתמשים ב'--' כדי לסמן שנגמרו ה-flagים. לכל פקודה יש פלאגים משלה, -f לא תמיד יעשה אותו דבר. בד"כ -v יהיה verbose, ו--help יעשה... הלפ.

Symbol Purpose Examples
- Short option (single letter) ls -l -ais the same as ls -la
-- Long option (full word) ls --all, tar --create --file=archive.tar
-- (without argument) End of options indicator rm -- -file (removes a file named -file)

echo

בגדול משתמשים בזה כדי שהאאוטפוט יהיה לפי מה שבא אחרי echo יכול להיות סתם string כמו היי עולם יכול להיות PATH$ כדי לדעת איזה תיקיות נכללות בפאת' (לוודא מה זה בדיוק אומר... אני יודע שאפשר להריץ דברים מהפאת' לא?)

echo -e כדי להתייחס ל-[[מילון BASH#Escape Sequences & Characters|Backslash Escaped Characters]] (אחרת ה-shell מתעלם ומדפיס אותם כמילוליים)

  • כדי להדפיס new line (שורה ריקה): \n echo -e "\n" אפשר גם באמצע הטקסט כמובן, בכל מקום שיופיע בתוך הסטרינג - תשבר שורה.

printf

  • דומה ל-echo אבל יותר מומלץ - לחקור ולהרחיב יותר קונסיסנטי במערכות שונות, תומך כברירת מחדל ב-Escape Characters חייב להשתמש בגרשיים (בניגוד ל-echo) הספר שלי אומר שהוא בעיקר שימושי בסקריפטים (כי אין STDIN), כדי לפרמט tabular data

  • חשוב לדעת שהיא לא מקבלת STDIN, אז לא עושים איתה פייפים

  • המבנה הכללי: printf [-v var] "format" arguments

- הפלט הולך ל-STDOUT אלא אם משתמשים בדגל -v var, ואז הפלט הולך לערך של משתנה var

printf "line1\nline2\n" אאוטפוט: line 1 line 2 (אם לא שמים שבירת שורה בסוף ה-path שבתחילת כל טרמינל יהיה על אותה שורה של line 2...)

  • הסיפור עם "FOAMAT" ו-ARGUMENTS הוא כזה:

    בגדול, "FORMAT" הוא הטקסט שיודפס, ו-ARGUMENTS הם ערכים שנגדיר עבור משתנים שישמשו אותנו ב-FORMAT

    המשתנים של printf נקראים conversion specifications והם מפורמטים עם % ואחריו אות

![[Pasted image 20250130015006.png|500]]

  • נניח שעשינו printf "...%s" אנחנו מגדירים אותו בסוף הפקודה כ-ARGUMENT, ונותנים string במקרה הזה printf "goodbye%s" hello יחזיר: goodbyehello

  • צריך לתת ARGUMENT ייחודי לכל conversion specificer, לפי סדר ההופעה שלהם בקוד אם נעשה פעם אחת 380, ונשתמש ב-4 specifiers, רק הראשון מביניהם יקבל את 380. אם כולם צריכים לקבל 380 צריך לסגור את הפקודה ב-380 380 380 380

  • אפשר לתת רכיבים נוספים ל-conversion specifiers: בגדול הפורמט הוא %[flags][width][.percision]conversion-specifier

    יש באמת יותר מדי אפשרויות לחרא הזה. אני שם פה את הפרטים:

![[Pasted image 20250130015807.png|500]] ![[Pasted image 20250130015820.png|500]]

file descriptor (FD)

  • המספר שמוצמד לסטרימים שונים ב-shell 0 זה STDIN 1 זה STDOUT 2 זה STDERR (error)

  • אפשר ליצור FDים חדשים באמצעות רידירקציה אנחנו עושים 2 כדי לקרוא/לכתוב ל-STDERR אז נעשה 3/4/5 כדי לקרוא/לכתוב ל-FD חדש (שנוצר אוטומטית)

STDERR & STDOUT

לטרמינל בעצם יש שני "סטרימים" של output, כברירת מחדל שניהם מופיעים על הטרמינל - STDOUT (1) - אאוטפוט של פקודות - STDERR (2) - הערות, שגיאות וכו' לגבי פקודות - אפשר להתייחס לסטרימים האלה בפקודות ולנתב אותם: הם נקראים פשוט 1 ו-2 בהתאמה - כל פקודה ששולחים לטרמינל מקושרת מראש ל-STDOUT שלה - המשמעות היא למשל ש- command > /path/to/file תשלח את האאוטפוט של הפקודה לקובץ Log שנגדיר בעצמנו. - לשים לב שהפקודה לא קשורה לסטרים שלה אם זה לא ממש אחריה בסינטקס. כלומר, כדי לאחד למשל את הסטרימים לאאוטפוט יחיד ולהוציא ל-log, לא עושים 2>1. עושים 2>&1 כדי שלטרמינל יהיה ברור שלא מדובר בקובץ אלא בסטרים מס' 1 - גם לא עושים &2>&1, כי בפעם הראשונה, כשזה מיד אחרי הפקודה, זה לא רלוונטי (מקווה שהבנתי נכון - לא חותם על המשמעות של ה-& ולמה רק בפעם השניה שמים אותו) - גם האינפוט של פקודות הוא סטנדרטי: STDIN (סטנדרט אינפוט)

/dev/null

אם אנחנו רוצים לנתב אאוטפוט כלשהו לטיזינבי, כלומר לא רוצים לעשות איתו כלום, אפשר לעשות נניח command 2> /dev/null זה מקום שמיועד להיות 'bit bucket', כלומר סתם מקום לשים בו שיט חסר שימוש

grep

לקחים מניסיון: תמיד כדאי לעשות grep -E כי פחות דברים דורשים אסקייפים (למשל {} של כמתים לא דורשות ב-ERE) כשעושים סוגריים של דיסיונקציה או השמטה ([], לא צריך לעשות אסקייפים בתוכן, אפילו לא לסוגר מרובע נוסף). לא ממש אפשר לשלב -o ו-v -- ה-v מחריג את כל השורה שבה הוא מוצא התאמה, אז o לא יעשה כלום... (במקום זה לעשות sed ולהחליף את התבנית שרוצים לגזור בכלום...)

[[מילון BASH#regular expressions (regexp)#|כל query של grep הוא למעשה regex]]

לשים לב שב-grep כמו ב-sed וכו', כל הארגומנט שאנחנו נותנים לו (regex-query בדוגמאות) עטוף ב-''

grep [options] regex-query [file...] המבנה הבסיסי

  • לתת filepath יבצע את החיפוש על קובץ
  • אפשרויות:
  • האפשרות הכי חשובה היא o- שמאפשרת לנו לקבל באאוטפוט רק את התחום של ההתאמה (ולא כל השורה) (משום מה זה צריך להיות הflag הראשון אם שמים כמה ברצף)
  • חשוב - -q כדי להשתיק את האאוטפוט לטרמינל בסקריפט
  • לרוב עדיף להשתמש ב-v- מאשר לנסות ליצור שלילה ב-regex
  • עוד אפשרות סופר חשובה: -F כדי לבטל regexp

![[Pasted image 20250127135638.png|500]]

  • אפשר לחפש בטווח של קבצים ע"י שימוש בגלובים ב-file path, ככה: grep bzip dirlist*.txt הולך לחפש את הסטרינג bzip בכל קובץ ששמו מתחיל ב-dirlist ונגמר ב-.txt

  • החיפוש הוא case sensitive

    command | grep "pattern" יביא את כל התוצאות שמכילות את ה-pattern בשם

command יכול להיות ls או משהו כזה

grep "error" filename.txt

יביא את כל השורות בקובץ שכוללות את error (או כל pattern אחרת)

ps aux | grep "firefox"

יראה לנו את כל התהליכים שרצים עם המילה firefox

אפשרויות:

אפשר להוסיף לכל use case בהתאם:

grep -i

בשביל שלא יהיה case sensitive

grep -n

בשביל תוצאות עם מס' השורה (אם מחפשים בקובץ)

grep -v

בשביל להפוך את החיפוש (רק תוצאות שלא כוללות את pattern)

egrep

  • רוב השנים, grep תמך רק ב-bre regexp, וכדי להשתמש ב-regex מסוג ere, הייתה לנו את התוכנה egrep
  • אבל, בגרסת ה-GNU הנוכחית של grep אפשר לעשות grep -e כדי להשתמש ב-ERE regex

zgrep

עם zgrep אפשר לבצע חיפוש בתוך קבצים דחוסים מסוג gz (סינטקס זהה ל-grep, נותנים את ה-gz ב-filepath)

Sort

מסדרת את הקלט לפי פרמטר שמגדירים ב-flag ![[Pasted image 20250111035848.png]] -m כל ארגומנט הוא קובץ לפני סידור. התוכנה תחבר את כל הקבצים לקובץ אחד שהיא תעשה לו סורט.

ממיין רק רשימות שבנויות בפורמט הבא: data=item1\nitem2\nitem3

אם יש לנו מבנה אחר אפשר לנסות להמיר: data="banana apple cherry"echo $data | tr ' ' '\n' | sort`

  • יש אפשרויות לכוון אותו לעמודות מסוימות בטבלה, לחלקים מסוימים בדאטה וכו' - לא היה לי כוח לקרוא עד הסוף.

עושים sort path.txt

tabular data

יכול להיות שזה רחב מ-sort ויעבור מקום בהמשך - בוא נניח שאנחנו רוצים לעשות sort לפי משקל קובץ לטבלה של ls -l - הפלט של ls l הוא טבלת דאטה: tabular data - בטבלה כזו, כל שורה היא record ולכל record יש מספר שדות - fields - אפשר לתת ל-sort ערך שנקרא key field על מנת שישמש כ-sort key - במילים אחרות, אפשר להגיד לו על איזה מספר של field להסתכל ב-table sort -k fieldnumber למשל sort -nrk 5 -n כדי לסדר מספרית -r כדי לסדר בסדר יורד -k 5 כדי לסדר לפי הערכים של שדה מספר חמש (במקרה של ls -l מדובר במשקל של הפריט)

  • כל string שמפורד ברווח או בטאב הוא field חדש מבחינת sort tony man הוא רקורד עם שני שדות - טוני ומן

sort --key=number[option] זה עוד סינטקס אפשרי sort --key=2n למשל כדי לחפש בשדה מספר שתיים עם האפשרות אן - כלומר סידור מספרי

  • ומה אם הקובץ שלנו משתמש ב-field separator אחר? לא רווח או טאב? אז sort נותן לנו להגדיר מה הוא ה-field separator באינפוט: sort -t 'fieldseparator' אם למשל זה קובץ /etc/passwd, שמופרד ע"י :, נעשה: sort -t ':' -k 7 /etc/passwd בדוגמה הזו אנחנו מחפשים בשדה השביעי שנוצר ע"י הפרדות של נקודותיים

multi-key sort

אפשר לסדר לפי מספר sort keys שונים ע"י שימוש ב--k מספר פעמים. בנוסף לזה, הערך של ה-sort key יכול להיות טווח של מספר שדות sort --key=start,end כדי להגדיר טווח sort --key=1,3 כדי להתחיל בשדה הראשון ולסיים בשלישי. באמצע נעבור בשני. sort -k 1,3 מניח שאפשר גם ככה

  • בנוסף, אפשר להגיד ל-sort לפעול עם offset - כלומר להסתכל רק על תו מסוים ב-string שמרכיב את ה-field, או על טווח של תווים: sort -k 3.7 כדי לסדר לפי התו השביעי בשדה השלישי sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt כדי לעשות multi key sort על מספרי התווים המפורטים בשדה 3, כולם עם האפשרויות n, b ו-r; היעד שמסדרים הוא קובץ הטקסט.

shuf

  • פקודה חמודה שמערבבת את סדר האותיות במילה - שימושי ליצרת ססמות למיניהן shuf <target>

uniq

מנקה כפילויות מרשימות ls /usr/bin | sort | uniq | less כדי להדפיס רשימה של יוזר בין, לסדר אותה, להוריד כפילויות ולקרוא את האאוטפוט עם less uniq -d כדי לקבל רשימה של כפילויות במקום להסיר אותן

  • חשוב לזכור ש-uniq עובד רק על רשימות שעברו sort, במובן שהוא מוחק כפילויות רק כשהן סמוכות
    a
    b
    c
    a 
    b
    c
    
    לא ינקה כלום.
    a
    a
    b
    b
    c
    c
    
    בלבד יחזיר לנו:
    a
    b
    c
    

![[Pasted image 20250127184339.png|500]]

nl

  • ממספר שורות, תוכנה ארכאית, עובדת כמו cat -n
  • מקבל מס' ארגומנטים cat argument argument כאשר כל ארגומנט יכול להיות קובץ או STDIN
  • מה שמייחד את nl הוא תמיכה ב-markup ארכאי (פרמוט 'תגים' שמתלבש על טקסט - marks it up) שפותח אפשרויות מספור נוספות
  • השפה בנויה על logical pages, שבנויים כמו HTML - יש להם header, body, footer
  • בכל page כזה המספור יכול להתחיל מחדש, ויכול לקבל חוקיות שונה.
  • השפה מבדילה בין החלקים באמצעות המארקאפ המשונה הבא, שאנחנו מוסיפים לקובץ שעליו נפעיל את הפקודה \:\:\: - ההתחלה של ה-header \:\: ההתחלה של ה-body \: - ההתחלה של ה-footer

    • כל אחד צריך להופיע בשורה נפרדת משלו
    • ברירת המחדל היא למספר את body בלבד! ולא למספר את החלקים האחרים.
  • יש כל מיני אפשרויות לפקודה שמשנות את אופן המספור ואת הפורמט שלו, מתוך ה-man: ![[Pasted image 20250130011912.png]]![[Pasted image 20250130011921.png]]

fold

  • מפרמט טקסט לשורות באורך אחיד
  • מקבלת אחד או יותר קבצי טקסט, ו/או STDIN fold -w 12 file.txt הולך לסדר את הקובץ כך שכל שורה תהיה בת 12 תווים, כולל רווחים ברירת המחדל היא 80 תווים fold -s כדי שהפקודה לא תשבור מילים אלא תפעיל את השבירה בסיום המילה הקרוב ביותר לאורך המוגדר

fmt

  • תוכנת טרמינל לפרמוט פסקאות. יכולה לעשות כמו fold ועוד הרבה יותר
  • השימוש הבסיסי: fmt -w widthnumber fileorSTDIN כדי לפרמט שורה לאורך של widthnumber תווים

    כברירת מחדל, fmt משמר בפסקה שלנו: רווחים בין מילים, שורות ריקות, אינדנטציה (רווחים בהתחלה איי גס) fmt -cw כדי למחוק אינדנטציות

  • כשאנחנו עושים fold עם fmt, יש לו הרבה פרמטרים חכמים שהוא עובד לפיהם: הוא משתדל לשבור בדיוק על סוף המשפט, לא ישבור משפט ממש בהתחלה או ממש בסוף, משתדל לשבור בסימני פיסוק

  • ה-fm קורא את כל הקובץ לפני שהוא מחליט איך לקפל אותו

![[Pasted image 20250130012855.png|500]]

  • fmt -p הוא הכי מעניין הוא בעצם אומר ל-fmt לפרמט רק שורות שמתחילות בסימן כלשהו, ולפרמט כך שהסימן יהיה בהתחלה של כל שורה באאוטפוט

    זה שימושי למשל כשרוצים לפרמט את ה-width של הערות בקוד בלבד - מתחילות ב-# fmt -w 40 -p '# '' sourcefile.txt

wslpath

  • ממיר path בפורמט ווינדוז ל-path בפורמט לינוקס ולהיפך wslpath -w "path" בשביל להמיר מלינוקס לווינדוז wslpath -u "path" כדי להמיר מווינדוז ללינוקס לשים לב שהפלט של wslpath משמיט את הגרשיים! עבור "path/path" נקבל בחזרה path\path וכו'
  • שימושי למשל כדי להעביר פאת'ים ממערכת WSL ל-explorer.exe כדי להריץ קובץ עם ווינדוז

Escape Sequences & Characters

  • מה שנקרא Backslash Escaped Characters הן בעצם אפשרויות פירמוט לטקסט שנעזרות ב-/ כדי לתת פקודות כמו לשבור שורה, ליצור טבלה וכו'. ![[Pasted image 20250118203325.png]]
With -e, escape sequences are interpreted: 
echo -e "Line1\nLine2" 
Output: 
Line1
Line2 
Adding tabs and alerts: 
echo -e "Column1\tColumn2\tColumn3\a"
Output: Column1 Column2 Column3 (and an alert sound)

**לא כל טרמינל מקבל את האסקייפים!!! נניח אצלי זה לא עובד... זה תמיד עובד אבל ב-printf.

sleep 10; echo -e "Time's up\a" טיימר פשוט עם התראה

הנה דוגמה לשימוש ב-conversion specifiers וב-escape characters כדי לפרמט דף HTML קצר עם printf:

printf "<html>\n\t<head>\n\t\t<title>%s</title>\n\t</head>\n\t<body>\n\t\t<p>%s</p>\n\t</body>\n</html>\n" "Page Title" "Page Content" <html> <head> <title>Page Title</title> </head> <body> <p>Page Content</p> </body> </html>

חידוד חשוב: בד"כ, ה-backslash escaped characters משתמשות ב- כדי להגיד "כאן אתה לא פשוט מדפיס טקסט, אלא קורה משהו מיוחד" יש לנו גרסאות מיוחדות של escaped characters, למשל כשאנחנו [[לינוקס - כללי#shell prompt#|מגדירים את ה-shell prompt]], במקרים האלה ה-backslash אומר משהו כמו "כאן אתה לא סתם מדפיס (אפילו אם אנחנו בתוך גרשיים) אלא עושה פונקציה מיוחדת בהתאם לאות שאחרי ה-backslash"

IFS

  • משתנה סביבתי שמשפיע על איך שה-shell מפרידה בין מילים באינפוט שהיא מקבלת המשמעות היא Internal Field Separator, ותווים שהוא כולל נחשבים דילימיטרים להפרדה בין מילים. כברירת מחדל, הוא יכלול רווח, טאב ו-newline character

  • אפשר לשנות את הערך של IFS כחלק מסקריפט, בשביל לאפשר לפקודה/פונקציה שצריכה לקבל מס' ארגומנטים בצורה של מילים נפרדות (למשל: Read) לקרוא אינפוט שמופרד באמצעים לא שגרתיים. למשל, עבור עיבוד של הקובץ /etc/passwd, או של המשתנה $PATH - שמפרידים ערכים באמצעות ":" - נשתמש בשורה הבאה כדי לאפשר הפרדה של הערכים: IFS=":"

  • לרוב לא נרצה להוסיף דילימיטר מוזר באופן קבוע ולכן עדיף להגדיר את IFS נקודתית עבור פקודה מסוימת, באמצעות "variable before command" (ניתן להגדיר באופן זמני משתנה אחד עבור פקודה, מגדירים אותו באופן סטנדרטי באותה השורה עם הפקודה, לפניה. כתוב על זה בקובץ, ניתן לקרוא עוד.)

eval

  • מבצעת פשוטה שהיא לכאורה די פשוטה:
  • מקבלת רשימה של ארגומנטים
  • מאחדת אותם ל-string אחד
  • מעבירה את הסטרינג כאאוטפוט ל-shell, ואומרת ל-shell להריץ את הסטרינג

  • כחלק משלב 2, הפקודה תבצע shell extensions (תרחיב פרמטרים, פ'אתים ו-tilde) בשונה מהרחבה רגילה, כאן היא תתבצע 'עד הסוף', כלומר עד שאין ערכים שניתן להרחיב, ולא פעם אחת בלבד (ה-default של ה-shell בשאר הזמן). ניתן להדגים את זה ככה: str=111; cmd="echo $str" היא פקודה שתחזיר 111. אם נריץ אותה **ואז נעשה**str=222, הערך של cmd יישארecho 111, וזה בגלל **שהמשתנה מוגדר פעם אחת** בהתאם לערך שהתרחב כשהוא הוגדר. אם היינו רוצים "לעכב" את ההתרחבות, כדי שידפיס **ערך עדכני** של str, **לא נוכל לעשות ככה:**str=111; cmd='echo $str' כי אז האאוטפוט יהיה $str... הבעיה היא שה-shell לא תרחיב את $str פעם נוספת לאחר שתקבל את הפקודה echo $str . וזה בגלל שה-shell מרחיבה כל דבר פעם אחת פר 'תנועה' (שימוש), ולא יותר. על מנת להרחיב פרמטר, פאת' וכו' עד למיצוי ההרחבות האפשריות, נשתמש בתכונה הזו של eval - בין שלב 1 לשלב 2, היא מרחיבה עד הסוף.

עריכה וקריאה

לערוך טקסט עם nano

nano path/file

כל המקשים שמראים ^ ליד אות, זה אות+ctrl כדי לשמור: write out (לרוב O^) ואז exit (לרוב X^)

tee

יודע להציג טקסט ולכתוב אותו בו-זמנית tail -f /path/to/rclone.log > /path/to/tail_output.log היא פקודה שתכתוב את הטקסט ללוג בלבד בגלל שכל האאוטפוט מכוון לקובץ. כדי להציג את המעקב אחר ה-log ובו-זמנית לכתוב אותו לקובץ אחר, יש להשתמש ב-pipe עם tee באופן הבא: tail -f /path/to/rclone.log | tee /path/to/tail_output.log

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

דיוק: tee יודעת לקבל סטנדרט אינפוט, ולהוציא אותו גם ל-standard output וגם לקובץ באותו זמן. היא שימושית כדי לחלץ דאטה מה-Pipeline שלנו תוך כדי שאנחנו ממשיכים לעבוד עליו. למשל ls /usr/bin | tee ls.txt | grep כדי לייצא לקובץ את האאוטפוט של ls אבל גם להעביר אותו ל-grep כדי לבצע סינון (שלא חל על הייצוא כי הוא בא לפני)

tail

לעומת cat שקורא את כל הקובץ בנקודה מסוימת בזמן, tail מראה לך את השורה (או כמה שורות) האחרונה בקובץ בזמן אמת (ממשיך לרוץ). שימושי כדי לעקוב אחרי logים, לרוב בשילוב עם grep כלשהו. tail -f /path/to/rclone.log כדי לעקוב אחרי קובץ tail -n <number> path כדי להראות את הנאמבר שורות האחרונות fail -f -n 5 path אפשר כמובן ביחד

head -n <number> path מראה את ה-איקס שורות הראשונות של הקובץ בלבד head -n 5 path בשביל החמש הראשונות וכן הלאה

cat

צריך לזכור לא להשתמש ב-Cat כדי לתת אינפוט ל-Grep... אפשר לעשות Grep -x query path-to-file ולהריץ אותו ישר על קובץ... זו טעות של טירונים.

cat filename.txt This will print the contents of filename.txt to the terminal.

  1. Concatenating Multiple Files cat file1.txt file2.txt This will display the contents of both file1.txt and file2.txt one after the other.

  2. Redirecting Output to a New File cat file1.txt file2.txt > combined.txt This will combine the contents of file1.txt and file2.txt and save them to combined.txt. If combined.txt already exists, it will be overwritten.

  3. Appending to a File cat file1.txt >> file2.txt This will append the contents of file1.txt to the end of file2.txt.

  4. Creating a File with cat cat > newfile.txt Then type the content of the file and press Ctrl+D to save and exit.

  5. Displaying Line Numbers cat -n filename.txt This will display the contents of filename.txt with line numbers at the beginning of each line.

  6. Suppressing Blank Lines The -s option suppresses repeated empty lines. If there are multiple blank lines in the file, it will only show one blank line: cat -s filename.txt

  7. Displaying Non-Printable Characters The -v option allows you to display non-printable characters, which can be useful for debugging or viewing control characters: cat -v filename.txt

אפשר לחבר מלא קבצים עם glob, נניח cat movie.mpeg.0* > movie.mpeg אם יש לנו מלא קבצים בשם movie.mpeg.001, 002, 003...

cat -A כדי לראות גם non-printing characters ^text אומר שזה tab text $ מסמל את סוף השורה האמיתי, כאן יש לנו trailing spaces שימושי לנושא של [[לינוקס - כללי#Text files#linefeed vs carriage return#|פורמט טקסט של DOS לעומת UNIX]]

less

less path קורא את כל הקובץ במסך חדש. ניתן לניווט עם מקשי וים יוצאים מ-less (ומהרבה דברים נוספים בטרמינל) בלחיצה על q זה אחלה כדי לקרוא קובץ בלי לזהם את הטרמינל less -n path כדי להראות מספרי שורות less -X path כדי שלא ירד מהמסך ב-q less +F path כדי שיעקוב אחרי שינויים כמו tail -f!!! - ב-less יש [[מילון BASH#Vim#|vim search]] מובנה, לוחצים / ומקלידים regex כלשהו. תומך ERE

Command Action
Down arrowEntere, or j Move forward one line.
Up arrow,y or k Move backward one line.
Space bar or f Move Forward one page.
b Move Backward one page.
/pattern Search forward for matching patterns.
?pattern Search backward for matching patterns.
n Repeat previous search.
N Repeat previous search in reverse direction.
g Go to the first line in the file.
Ng Go to the N-th line in the file.
G Go to the last line in the file.
p Go to the beginning of fthe ile.
Np Go to N percent into file.
h Display help.
q Exit less.
### awk
כמו grep ועוד כמה - זה לא חלק מ-bash. זה Unix shell tool שיכול להיות די שימושי לבאש.
כלי עוצמתי שמאפשר לנו לחפש, לחלץ ולערוך טקסט מתוך דאטה מסוימת. יודע לעשות
- Pattern Matching: Processes only the lines of input that match a given pattern.
- Field Extraction: Allows you to extract specific fields (columns) from a line of text.
- Text Transformation: Modifies text based on conditions or manipulations.
- Arithmetic Operations: Can perform calculations on numerical data.

awk 'pattern {action}' file Pattern = התנאי שבוחרים שורות לפי (לא חובה) Action= פעולות לבצע על השורות שנבחרו file = פאת' לדאטה שעובדים עליה

awk '{print $1, $3}' file.txt להדפיס עמודות 1 ו-3

awk '/error/ {print $0}' file.txt

להדפיס רק מה שכתוב עליו error

`echo -e "10 20\n30 40" | awk '{print $1 + $2}'

מחבר את 10 ו-20, 30 ו-40

אפשר להשתמש ב-awk עם [[מילון BASH#pipe|pipe]] כדי לבצע אותו על האאוטפוט של הרישה

`zypper packages --orphaned | awk '/^i/{print $3}'

/^i/: Matches lines that start with the letter i. {print $3}: Prints the third column (package name). This extracts the names of orphaned packages from the zypper output.

מדריך מלא

Vim

אני קורא את 'the linux terminal 6th edition' והספר החליט שצריך ללמוד קצת על vi ועל vim - POSIX (הסטנדרט של הלינוקס טרמינל) דורש שיהיה vi - vi(m) keys - כיום רוב המערכות באות עם VIm שהוא שדרוג גדון/ל -Vi, והן מקשרות את הפקודה Vi ל-Vim באמצעות alias או sym link

לצאת ולהיכנס

כשאנחנו ב-Vim, אנחנו יכולים לכתוב פקודות. Enter כדי לשגר את הפקודה. :q לצאת :q! לצאת אם הוא עושה לך בעיות :wq לצאת ולשמור :w filename.txt כדי לשמור בשם ZZ בנורמל מוד גם שומר ויוצא vim pathtonewfile.txt כדי להריץ עם קובץ חדש (זו דווקא פקודת bash ולא פקודת vim)

פונקציות עריכה בסיסיות בסיסיות

  • כשרק פותחים קובץ, אנחנו על normal mode! זה אומר שכל מקש הוא פקודה ושאנחנו נעשה מלא בלאגן אם נתחיל להקליד.
  • כדי לכתוב, צריך ללחוץ i ולעבור ל-INSERT MODE
  • ESC כדי לצאת מאינסרט מוד
  • כדי לתת פקודה, אנחנו צריכים להיות על normal mode (כלומר לא על אינסרט) ואז ללחוץ : (נקודותיים).
  • כדי לשמור את הקובץ עושים :w (כתיבה)
  • ב-vi רגיל קומנד מוד נקרא ex mod
  • קיצורים ב-normal mode: ![[Pasted image 20250125010136.png]]

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

  • אפשר להוסיף מספר לפני כמעט כל פקודה כדי להריץ אותה n פעמים ברצף, למשל 6l יקח אותנו ימינה שישה צעדים

  • ב-normal mode, לוחצים u בשביל לעשות undo לשינוי האחרון שביצענו (קונטרול זי)

  • הספר טוען שבאמצעות i (כניסה לאינסרט מוד) אי אפשר ללכת לסוף השורה כדי לעשות append לטקסט. אני רואה שאצלי כן אפשר... אבל בכל אופן אפשר ללחוץ A (גדולה) כדי ללכת לסוף של שורה ולעשות append לטקסט באינסרט מוד. ואפשר לעשות a קטנה כדי לעשות append לטקסט רווח אחד אחרי איפה שעמדנו במקור.
  • אפשר להשתמש ב-o או O (גדולה/קטנה) כדי לפתוח שורה חדשה - קטנה זה מתחת לשורה הנוכחית, וגדולה זה מעל.
  • אפשר למחוק באמצעות x, או number x כמו שלמדנו, כדי למחוק number תווים
  • עוד פקודת מחיקה היא d, גם לפניה אפשר לשים מספר, ואחריה תמיד תבוא אות נוספת שתגדיר חוקיות למחיקה

![[Pasted image 20250125011619.png|500]]

  • כשאנחנו משתמשים ב-d אנחנו למעשה עושים cut, כלומר שהמידע שמחקנו נשמר ב-paste buffer (קליפבורד).
  • אפשר להשתמש ב-p כדי להדביק אחרי העכבר וב-P גדולה כדי להדביק "רגיל" (אחרי העכבר).
  • אפשר להשתמש גם ב-y כדי לעשות "yank" (קופי, במקום הפייסט של d) ![[Pasted image 20250125012008.png|500]]

  • משום מה, אם עושים p כשנמצאים על G (תחילת השורה האחרונה) זה מדביק בשורה חדשה למטה... (זה בעצם לא משנה איפה נמצאים, להעתיק שורה מדביק שורה חדשה)

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

  • אם אנחנו על Line 3 ועושים J, אנחנו נחבר אליה את זו שמתחתיה. כלומר, נקבל Line 3 Line 4 בשורה אחת

חיפוש והחלפה

בשורה ספציפית: - אפשר לעשות f כדי לחפש אות בתוך השורה הנוכחית ולקפוץ אליה (נגיד f a כדי לקפוץ לאות a) - אפשר לבצע את אותה קפיצה שוב באמצעות ; בכל הקובץ - אפשר לחפש string (ולא רק אות בודדה) בכל השורות בקובץ באמצעות / ואז ה-string - זה כבר החיפוש שאנחנו מכירים מ-vimium ומעוד תוכנות - אפשר לבצע את אותה קפיצה שוב באמצעות n - אפשר להשתמש ב-regexp בחיפוש הזה - תומך BRE find & replace על כל הקובץ - את זה עושים באמצעות command mode (לוחצים : ומקלידים) ![[Pasted image 20250125013045.png|500]]

  • אפשר gc בסוף במקום g כדי שיבקש מאיתנו confirmations. שולטים בהם ככה:

![[Pasted image 20250125013205.png]]

פעולות על קבצים מרובים

vi(m) file1 file2 file3 כדי לפתוח מספר קבצים אנחנו נראה רק את הקובץ הראשון :bn כדי לעבור לקובץ הבא :bp כדי לחזור לקודם - וים לא מרשה לעבור קובץ אם לא שמרנו קודם. כדי להתעלם ולעבור בכל זאת, עושים את הפקודה עם סימן קריאה בסוף, למשל :bn! :buffers כדי להציג רשימת קבצים בתחתית :buffer filenumber כדי לעבור לקובץ שעל באפר במספר שאנחנו מציינים (הפקודות bn ו-bp הן קיצורים ל-buffer next ו-buffer previous)

:e filename כדי להוסיף קובץ לאחר שכבר פתחנו אחד או כמה (את שם הקובץ או את ה-Path המלא אליו? מה ה-Path של וים?)

  • אפשר לעשות את כל הפעולות של yank ושל d "בין קבצים", כלומר להעתיק, לעבור קובץ, להדביק וכו'
  • אפשר להשתמש ב- :r filename כדי להכניס מתחת ל-cursor שלנו את כל התוכן של filename (שוב, האם זה שם או path???)

clear

מנקה את הטרמינל מאאוטפוטים קודמים

concat

מחבר רשימות יחדיו concat foo.txt bar.txt > foobar.txt יש לי man concat בטרמינל אבל גם ג'יפיטי וגם מייקרוסופט מתעקשים שאין פקודה כזו... אפשר לחבר רשימות גם עם cat...

sed

לשים לב שב-sed כמו ב-grep, כל ה-expression (ארגומנט, וואטאבר) שניתן לפקודה עטוף ב-'' אלא אם משתמשים במשתנים! אם אנחנו צריכים ש-$var' יתרחב לנו ב-query, נעטוף הכל דווקא בגרשיים כפולות " .

המשמעות היא Stream EDitor - יודע לקחת אאוטפוט של פקודות ולעשות עליו פעולות, קצת דומה ל-awk - תוכנה מורכבת שנכתבו עליה ספרים שלמים... - היא יודעת לקבל STDIN או שם של קובץ script עם פקודות בתוכו. בעצם כל מה שמהווה פלט של פקודה. - היא מחילה את הכלל שנותנים לה על כל שורה בפלט בנפרד ![[Pasted image 20250119193916.png]]

  • התוכנה sed יודעת מה הם המשתנים שלה, ולכן מאפשרת להשתמש בכל תו כדלימיטר! נהוג להשתמש ב-/ אבל זה יכול להיות כל תו אחר כל עוד האפשרויות מפורמטות נכון.
  • אפשר לתת לכל פקודה של sed "כתובת" שמפרטת על איזה שורה לבצע את הפעולה. ברירת המחדל אם לא מציינים היא על כל השורות.
  • האפשרות בסוף (איפה שלרוב יש g) קובעת על איזה חלק של השורה נפעל. g אומר על כל התאמה. ברירת המחדל היא רק על ההתאמה הראשונה. sed 1s/front/back ישנה את פרונט לבק רק בשורה הראשונה של האאוטפוט
  • יש די הרבה דרכים מעניינות לפרמט את הכתובת: ![[Pasted image 20250128033203.png|500]]

  • אם משתמשים ב--p כדי פשוט להדפיס טווח מסוים, חייב להשתמש ב--n כדי לבטל את ההדפסה כברירת-מחדל של כל השורות באאוטפוט

  • פקודות עריכה בסיסיות ל-sed: ![[Pasted image 20250128033529.png|500]]

שימוש נכון ב-sed s

  • הפורמט הבסיסי של sed s הוא s/regexp/replacement/
  • הפקודה sed s/b/B תחליף רק את ה-b הקטנה הראשונה בכל שורה אם אנחנו רוצים להחליף כל הופעה של regex ב-replacement, נשתמש ב-/g, כך: sed s/b/B/g

  • שימושי מאוד עבור sed s - שימוש ב-[[מחברות קוד/Bash&Linux 🐧/מילון BASH#regular expressions (regexp)#subexpressions, backreference#| regexp subexpressions]]

  • יש כלים מורכבים יותר מ-sed, שהם כבר ממש שפות תכנות עצמאיות: מדובר ב-[[מחברות קוד/Bash&Linux 🐧/מילון BASH#awk#|awk]] וב-perl פרל היא מאוד רחבה ומשמשת גם לכתיבה ב-web ו-awk חזקה מאוד במניפולציה של tabular data

עוד אפשרויות חשובות

  • יש sed -i (דגל i, לא לבלבל עם הפעולה i/...) - זה אומר ל-sed לבצע את כל השינויים על הקובץ שסיפקנו לו, במקום לייצא לאאוטפוט

  • אפשר לתת ל-sed כמה פעולות יחד, מופרדות ב-; sed -i 's/lazy/laxy/; s/jumps/jimps/' foo.txt זה יגיד לסד לבצע את שתי ההחלפות הבאות על הקובץ פו, ולשמור אותן ישר על הקובץ

wc

wc pathtofile כדי לראות כמה שורות, מילים ובייטים יש בקובץ wc -l כדי לספור שורות בלבד (טוב כדי לדעת כמה פריטים יש ברשימה) command | wc -l זו דרך פופולרית לספור פרטים באאוטפוט של פקודה

גלובים:

  • ? - אומר "תו לא ידוע", זה קצת מבלבל אבל בגדול זה placeholder.
    אם נגיד אני רוצה לפרט את כל הקבצים בתיקיה שהם txt ושיש להם 4 אותיות בשם, אני עושה ls *.txt???? אפשר גם לדעת חלק מהאותיות: ls system*????.txt ימצא את כל מה שיש לו 4 אותיות ומתחיל בsystem (והוא מסמך טקסט).

  • כוכבית * - המשמעות היא any string ואפשר לשחק עם זה בכל מיני דרכים ![[Pasted image 20250105135032.png]]

![[Pasted image 20250124005850.png]]

![[Pasted image 20250124005956.png]]

קבצים חבויים:

לעשות echo .* יתן לנו את כל הקבצים החבויים, אבל גם את התיקיה הנוכחית ואת התיקיית אם שלה, שהן . ו-... כדי לקבל תוצאה בלעדיהן, צריך לעשות echo .[!.]*, שזו תוצאה שמתרחבת לכל קובץ שמתחיל בנקודה ולאחריו בא לפחות תו אחד. ls -A נותן לנו את התוצאה הנכונה בקלות

cut

  • פקודה שיודעת לחלץ חלק משורת טקסט ולהוציא את האאוטפוט ל-STDOUT
  • יכולה לקבל כמה קבצים או STDIN ![[Pasted image 20250127185350.png|500]]

cut -f fieldnumber path\to\file כדי לחתוך שדה מסוים בטבלה, שמופרד ברווחים או ב-^ טאבים cut -c rangestart-rangeend כדי לחתוך את התווים בריינג' הזה של כל שורה cut -c 7-10 כדי לחתוך את התווים השביעי עד העשירי בכל שורה (אם מדובר בתאריכים שרשומים ככה MM/DD/YYYY - מדובר בשנה בלבד בכל שורה) cut -c9- כדי לחתוך מהאות התשיעית עד סוף השורה בכל שורה - אפשר להקדים פקודת -f לקאט (בחירת שדה) בפקודת -d כדי לבחור delimiter שאינו רווח או טאב (delimiter הוא התו שמסמל מעבר שדה) cut -d ':' -f 1 /etc/passwd כדי לחלץ את השדה הראשון של כל שורה בקובץ (הקובץ מפורמט עם : בין שדה לשדה)

expand

  • אם יש לנו טקסט שמפורמט עם tabs (field1^field2^field3$)
  • אנחנו יכולים להפעיל עליו cut באמצעות שדות בלבד cut -f
  • כדי להפעיל cut -c במקום - אנחנו צריכים להחליף כל non-printable character מהסוג של tab ^ ברווח - כדי ליצור הפרדה של fields שניתן לספור כתווים.
  • expand sourcefile כדי להפוך טקסט עם טאבים לטקסט עם רווחים במקום
  • הפקודה expand מקבלת גם STDIN
  • יש פקודה מקבילה unexpand שעושה את ההיפך - הופכת רווחים לטאבים
  • שתיהן שייכות ל-GNU coreutils

paste

  • תוכנת טרמינל שמקבלת כאינפוט: ריבוי אגרומנטים של file paths ו/או STDIN
  • ומוציאה כאאוטפוט: חיבור של הטבלאות שבהם.
  • לא מדובר ב-append לכל טבלה ככה שהן מופיעות אחת אחרי השניה, אלא בהוספה של עמודות כך שטבלה אחת תכיל את השדות של שתי הטבלאות (או יותר)
  • למשל טבלה עם עמודות abc וערכים בכל עמודה; וטבלה עם עמודות abc וערכים בכל עמודה; תהפוך לטבלה אחת עם abcdef עמודות בהתאם paste filepath filepath צריך לנתב לקובץ או שזה יוצא לטרמינל

join

  • דומה ל-paste במובן שהוא מוסיף עמודות לקבצי טקסט
  • אבל שונה באופן שהוא פועל: join קשור ל-relational databases, שהם מבנים בהם מספר טבלאות עם shared key משולבות כדי להגיע לתוצאה הרצויה
  • אז בגדול join מחברת דאטה ממגוון קבצים לפי shared key field
  • הסבר: כדי שנוכל לחבר שתי טבלאות במגוון דרכים, צריך שיהיה להן שדה אחד שהוא זהה, במובן שהוא מחזיק ערכים מאותו הסוג והמפתח. לא חייב שכל הערכים יהיו קיימים בשתיהן, אבל נניח שבשתיהן יש שדה NUMBER ובשתיהן יש שורה שהערך שלה עבור NUMBER הוא 1 - אז מבחינת ה-relational database מדובר בשורות 'תואמות'.
  • אז נניח דוגמה כזו: בטבלה אחת יש מס' לקוח, שם פרטי ושם משפחה; בטבלה שניה יש מס' הזמנה, מס' לקוח, כתובת רחוב, פרטי הזמנה, מועד הזמנה; על ידי התאמת הערך המשותף "מס' לקוח", אנחנו יכולים לשלב את שתי הטבלאות, כדי ליצור למשל טבלה שמכילה: שם פרטי, שם משפחה, פרטי הזמנה, מועד הזמנה.
  • כדי ש-join תעבוד כהלכה - הטבלה צריכה להיות sorted לפי ה-shared key field join table1.txt table2.txt כאשר לטבלאות יש שדה משותף, יוצר חיבור של כל השדות לפי השדה המשותף,
  • הפקודה join רק מחברת את כל הערכים, ואז אפשר לגזור מההאוטפוט שלה טבלה חדשה עם ערכים ספציפיים בלבד
  • חשוב לדעת: הפקודה join מתייחסת ל-whitespace כ-delimiter באינפוט, אבל מדפיסה באאוטפוט את הדילימיטר כ-single space. ב-man join יש הסבר איך לציין דלימיטר אחר לשדות.

tac

  • tac filepath1 filepath2...
  • מחזירה פלט כמו של cat אבל ליטרלי הפוך
  • במובן שהשורה האחרונה תודפס ראשונה, וכן הלאה
  • שימושי כדי להפוך קבצי log - כדי שהשורה העדכנית ביותר שלהם תופיע בראש הרשימה

rev

  • מקבלת string והופכת את סדר התווים שלו
  • יכולה לקבל קובץ או STDIN
  • למה לעזאזל זה שימושי? למשל כדי להסיר נקודות ממשפטים בקובץ. תחשוב על זה: אין לדעת אחרי כמה תווים תופיע הנקודה בכל שורה. אבל אנחנו יכולים לדעת שהנקודה היא התו הראשון בכל שורה אם נהפוך את כל התווים. אז כדי להעיף את הנקודות נעשה rev לקובץ, נעשה cut -c 1 כדי להעתיק כל שורה החל מהתו השני לקובץ חדש, ואז נעשה rev לקובץ החדש כדי לחזור לסדר התווים הנכון.
  • אפשר לוותר על קבצים ולעשות את כל התהליך הזה ב-shell stream, בפייפ די פשוט:
  • rev | cut -c number | rev

comm

  • פקודה שמשווה בין קבצי טקסט, מראה שורות שיש להם במשותף ושורות ייחודיות לכל קובץ
  • comm file1 file2 מחזירה אאוטפוט עם שלושה שדות: a זה שורות שייחודיות לקובץ הראשון b זה שורות שייחודיות לקובץ השני c זה שורות משותפות אפשר לעשות comm -n ולציין מספרים: 1, 2 ו-3 בשביל abc בהתאמה כל מספר שציינו "מושתק" - כלומר מושמט מהטבלה של האאוטפוט comm -12 כדי להדפיס את c בלבד comm -2 כדי להדפיס את a ואת c בלבד וכו'

diff

  • בדומה ל-comm', משווה בין אוסף של קבצים. אבל מדובר בכלי מורכב יותר: הוא יודע לבדוק source trees - כלומר להשוות folder structures על הקבצים שבהם; הוא יודע לשמור diff file, ידוע גם כ-patch, שפקודות כמו patch יכולות להשתמש במידע שבו כדי להמיר קובץ מגרסה אחת לאחרת שלו.
    diff file1 file2... כדי להשוות קבצים
  • האאוטפוט קצת מיוחד: הוא בעצם מסביר איך להגיע מהקובץ הראשון לקובץ השני. השינויים בין הקבצים מחולקים לקבוצות של change commands שהמבנה שלהן הוא range operation range. כלומר, איזה טווח בקובץ 1 צריך לעבור איזה שינוי כדי להפוך לאיזה טווח בקובץ 2. מפרמטים ככה: ![[Pasted image 20250128025123.png|500]]

  • כשאומרים לנו לתת range, הכוונה היא לרשימה מופרדת בפסיק של שורת ההתחלה ושורת הסיום. נניח 1,3 בשביל טווח שהוא משורה 1 עד שורה 3

  • הפורמט הזה הוא סטנדרט ה-POSIX, אבל הוא לא הכי נוח, לכן יש עוד אפשרויות: diff -c file1 file2... יציג את ההבדלים בפורמט context
  • האאוטפוט של context ימנה את הקבצים המשווים, ואז יציג כל קובץ עם ribbon אחר, כאשר כל פריט באאוטפוט יהיה ציון של השורות הרלוונטיות מוקפות ב-ribbon של הקובץ המתאים, עם שינויים מפורטים בפורמט הבא: ![[Pasted image 20250128025517.png|500]]

diff -u file1 file2... יציג את הדברים בפורמט unified - האאוטפוט דומה לשל context אבל יותר מתומצת: הוא מונה את הקבצים, ואז בכל פריט עוטף בשטרודלים @ את טווחי השורות הרלוונטיים לכל הבדל בין קובץ 1 לקובץ 2, עם סימון של מינוס או פלוס כדי להגיד איפה השינוי נוסף ואיפה הוא נגרע. - מתחת, נקבל פירוט של השינוי. ![[Pasted image 20250128025757.png|500]]

patch

  • משמשת כדי לקחת קובץ אאוטפוט של diff ולהשתמש במידע שבו כדי להמיר קובץ מגרסה ישנה לגרסה חדשה יותר
  • מקרה שימוש לדוגמה: כשמישהו כותב עדכון לקרנל של לינוקס, זה לא הגיוני שישלח למפתחים האחרים את כל הקבצים של הקרנל בגרסה שהוא יצר. הם עובדים על אותו בסיס. אז הוא שולח להם diff file של השינוי בין האפסטרים של הקרנל לגרסה המעודכנת שהוא כתב, ומפתחים אחרים יכולים להריץ אותו עם פקודת patch כדי לעדכן את הקרנל שלהם לגרסה שהבחור כתב.
  • יש המלצה רשמית של GNU לגבי איך להכין קובץ diff במטרה לעדכן איתו קובץ: diff -Naur old_file new_file > diff_file הקבצים יכולים גם להיות תיקיות.
  • כדי להריץ את העדכון, אנחנו פשוט עושים patch < diff_file (הוא צריך שיהיה לו את הקבצים כפאת'ים, או להיות באותה תיקיה עם הקבצים ולקבל את השמות שלהם)
  • גם לא צריך לכתוב לו איך יקרא הקובץ המעודכן - יש לו את השמות מתועדים ב-diff file

tr

  • נועד לבצע טרנסליטרציה בקבצים - כלומר, להחליף סוג מסוים של תווים בסוג אחר של תווים.
  • נניח, להעביר את כל האותיות מ-lowecase ל-uppercase tr find-range replace-range tr a-z A-Z כדי להחליף כל אות קטנה בגדולה
  • מקבל STDIN ומחזיר STDOUT
  • יכול לקבל בערכים שלו: או רשימה בלי דלימיטר ABCD; או טווח A-Z; או עם POSIX CHARACTER CLASS [ :upper: ]
  • לזכור לגבי [[מחברות קוד/Bash&Linux 🐧/מילון BASH#regular expressions (regexp)#Character Classes#|character classes וכל החרא של POSIX|]]

tr find-range replace-range < source-file > destination-file אפשר לציין קובץ לחפש בו וקובץ לייצא אליו אאוטפוט tr -d find-range כדי פשוט למחוק טווח כלשהו tr -d '\r' < dos_file > unix_file לדוגמה - מוחק carriage return מקבצים שמפורמטים עבור DOS. (ה-/r הוא ה-carriage return, אנחנו משתמשים כאן בערך אבסולוטי ולא בטווח - אבל זה נופל באפשרות הראשונה לאינפוט, כלומר רשימה - תחמנו איבר עם גרשיים).

  • דוגמה מעניינת: יש שיטה ל-"הצפנה" של טקסט בשם ROTI13: העיקרון שלה היא לקחת כל תו ולהזיז אותו 13 מקומות למעלה; מאחר שיש 26 אותיות באלף-בית, לבצע ROTI13 בפעם השניה פשוט יחזיר את הטקסט לצורה המקורית.
  • אפשר לבצע ROTI13 עם tr באופן הבא:
  • tr a-zA-Z n-za-mN-ZA-M
  • בעצם אמרנו ל-tr לקחת כל תו בטווח של a-z או של A-Z ולהחליף אותו בטווח n-z (אן היא האות ה-13) או a-m (החצי הראשון של הטווח, לאחר שביצענו את הקפיצה) או N-Z ו-A-M (הצורות הגדולות)
  • אני יכול להבין איך tr מבין את "ההיפוך" של המספור באמצע הרשימה - ברגע שנגמרים לו הטווחים להצמיד ל-a-z באמצעות n-z הוא עובר ל-a-m.
  • אבל איך הוא יודע לבד להחליף לפי הקייס?
  • אני מניח שזה לפי מתי שנגמרים לו הערכים - כלומר להחליף את a-z דרש ממנו את כל הערכים של n-z ו-a-m, ואז הוא עובר ל-N-Z ו-A-M וממצה אותם רק ברגע שהוא החליף את הערכים של A-Z

tr -s find-value יכול למחוק מופעים חוזרים של הערך שהוא מקבל נקרא -s כי הוא עושה "squeeze" rs -s ab למשל ימחק כל מופע חוזר של אייבי, עד שישאר מקסימום אחר aaabbbccc יחזיר abccc - כדי שהניקוי יעבוד, המופעים של הפרטים ברשימה (ה-value הוא רשימה בדוגמה הזו) חייבים להיות סמוכים.

aspell

  • תוכנה אינטרקטיבית לבדיקת איות
  • קדמה לה ispell שיכולה להחליף אותה לא רע
  • בד"כ היא משמשת תוכנות אחרות (זה ה-spellchecker של המערכת), אבל ניתנת גם לשימוש עצמאי בקומנד ליין
  • יודעת לבדוק טקסט, HTML, סי ו-CPP ועוד aspell check textfile

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

  • כברירת מחדל, aspell יוצר קובץ גיבוי לאחר השמירה, עם הגרסה המקורית ששלחנו לו. הוא קורא לקובץ textfile.bak ניתן להגיד לו שלא ליצור גיבוי עם הדגל --dont-backup

  • אם אנחנו רוצים שיתעלם מ-HTML TAGS (שיסומנו לרוב כשגיאות), נעשה aspell -H

  • ה-aspell מתעלם אוטומטית מכתובות מייל ומ-URLים. אפשר לשנות את זה באפשרויות שלו - נחסוך את זה מפה, אפשר לחפש ב-man aspell

split

csplit

sdiff

groff

קשור [[מחברות קוד/Bash&Linux 🐧/לינוקס - כללי#Document Formatting Systems|לפרמוט טקסט באמצעות roff]] - ה-groff היא האימפלמנטציה של פרמוט markup מסוג roff שקיימת ברוב המערכות כיום - היא אוספת מגוון אפשרויות של roff ל-macro packages שמאפשרות שימוש קל בהן

mandoc היא מאקרו-פקג' שאחראית על הפרמוט של דפי man אנחנו יכולים לדמות פקודת man command על ידי הצבעה על ה-manpage file הרלוונטי ושימוש בפקודת groff עם mandoc, ככה: zcat /usr/share/man/man1/ls.1.gz | groff -mandoc -T ascii החלק של -T ascii קובע את ה-output driver של הפקודה. אנחנו רוצים ASCII Characters ברירת המחדל היא PostScript החלק של zcat נועד פשוט לקבל כא#אוטפוט לפייפ את התוכן של ה-gz file

tbl

  • ה-tbl היא תוכנה נוספת שגורמת ל-groff לפרמט טבלאות לפי שפת ה-markup של tbl
  • groff -t כדי להפעיל אותה
  • להבדיל מ-groff -T driveroutputformat
  • היא פועלת על קבצי .tbl
  • אפשר לתאר איפה מתחילה ונגמרת הטבלה ואיך יוצג כל חלק בה - לעשות גוגל אם צריך...

  • אם נשתמש ב-PostScript output נוכל לראות את הטבלה הרבה יותר יפה מאשר בטרמינל!

bc

  • קיצור של base calculator - התוכנה יודעת להמיר מספרים (סטרינג של ספרות ללא רווחים) מבסיסים שונים. בסיס הוא כמות הספרות שקיימות בשיטת הספירה שלנו, כלומר, הספרות שמהן ניתן להרכיב מספרים, כולל 0. בסיס 2 הוא ספירה בינארית: יש לנו 0 ו-1, לכן 0 תהיה הספרה הראשונה, ו-1 תהיה הספרה השניה. לא לשכוח שהמחשבון ממיר מספרים ולא ספרות - 111 לא יהפוך ל-222 (המרה של כל אחד בנפרד), אלא ל-7, מאחר ש-111 הוא הצירוף השביעי שניתן להרכיב מספרות בינאריות, כשמתחילים מ-0 וממשיכים ל-1 ואז ל-01 ואז ל-11 וכן הלאה. file_octal=$(echo "ibase=2; obase=8; $file_binary" | bc) המרה של מספר בינארי למספר אוקטאלי bc ibase=num obsase=num target כאשר i אומר input (או STDIN או משהו שנציין ב-targer), ו-o אומר output (הבסיס של תוצאת ההמרה)

  • אפשר להריץ את bc כמחשבון אינטרקטיבי באמצעות הדגל -q אחרי שמריצים עם קיו, אפשר לעשות למשל 2 + 2 ולקבל את התשובה quit כדי לצאת מהמחשבון


bc scripts: - התוכנה bc למעשה פועלת על קבצים או אינפוטים שכתובים בשפת הסקריפטים שלה, שהיא C-like כשאנחנו כותבים לה את הסינטקס הבסיסי של obase, ibase וכו' אנחנו למעשה כותבים בשפה הזו.

  • נשתמש בדגל -q כדי להשתיק את ההודעה על זכויות היוצרים באאוטפוט
  • את הסקריפט אפשר לתת ל-bc גם בצורה של redirection < או בצורה של here string >>>

  • יסודות השפה: לקובץ הסקריפט נקרא name.bc

כדי לפתוח הערה עושים /* היא יכולה להמשיך כמה שורות, וסוגרים אותה ידנית ב: */ (זה כמו ב-C)

כדי לבצע חישובים פשוט כותבים a + b וכך הלאה - bc תחזיר את התשובה

טוב, אלה מעט מאוד יסודות. הספר ציין באקראי גם שיש פקודה של scale = num שקובעת כמה אפסים יהיו לנו לפני הנקודה (האם נראה 1 או 01 או 001 וכן הלאה). כדאי לציין כאן שהאופרטורים המתמטיים נכתבים שונה מב-shell ויותר דומה לשפת C - אם רוצים אי פעם לעשות חישובים מורכבים יחסית, לבדוק באינטרנט איך עובדת השפה של bc...

פקודות, משתנים, קונפיגורציה

$() פלט כמשתנה

command $() מבצע את הפקודה על משתנה 'ספונטני' שהוא האאוטפוט של הפקודה בסוגריים למשל sudo zypper remove $(zypper packages --orphaned | awk '/^i/{print $3}') כדי לעשות remove לתוצאה של: "זיפר, תפרט איזה חבילות לא-דרושות יש | awk, תלך לכל השורות שמתחילות ב-i (installed), ותדפיס את עמודה 3" (התוצאה תהיה כל הטקסט בכל עמודה 3 עבור כל שורה שמתחילה ב-i, ואז זיפר יבצע על הרשימה הזו remove)

.bashrc

נמצא ב- ~/.bashrc כדי להוסיף PATH ל-$PATH מוסיפים לקובץ: export PATH="Path/To/Path:$PATH"

כדי לטעון מחדש את ההגדרות עושים: reload ~/.bashrc

(אפשר להריץ קבצים מתיקיות שנמצאות ב-path, ע"י כתיבת השם שלהם)

variables

global variables

  • משתנים שמשותפים לכל התוכנה/סקריפט, משתנים "רגילים"

  • לבאש לא אכפת איזה סוג של דאטה יושב במשתנה שלנו, הוא מתייחס להכל כ-string בהמשך לזה, הערך של המשתנה יכול לכלול כל ביטוי שהוא טקסט, או שיכול להתרחב מתוך טקסט (כל expansion יכולה להגדיר משתנה, כמובן...)

    x=$(command) מגדיר לנו משתנה לפי התוצאה של פקודה (זה שימושי גם לעוד דברים, לא רק להגדרת משתנה)

    כדי לבצע חישובים על משתנה נשתמש במה שהכרנו כ-[[מילון BASH#Arithmetic expansion|Aritmethic Expansion]], ככה: x=$((5 * 7))

    בנוסף אפשר להשתמש ב-Escape sequences כמו \n בשביל newline ו-\t בשביל טאב

צריך להוציא את ההסבר המלא מ-GPT בגדול, לא צריך להכריז על משתנה. עושים myvar = "..." ואז כדי להשתמש בערך של myvar, עושים $myvar

  • הטרמינל מאוד chill לגבי משתנים, אם עשינו $var ולא הגדרנו את var, הוא פשוט יוצר את var ללא ערך! כלומר שלעשות echo $var לא יחזיר שגיאה, אלא פשוט יחזיר אאוטפוט ריק.

  • יש חוקים לגבי השם של משתנה: הוא יכול לכלול רק אותיות, מספרים וקו תחתון (אי אפשר להשתמש בדאש במקום...) האות הראשונה חייבת להיות אות או קו תחתון, כלומר, לא מספר. אסור להשתמש ברווחים!!! או בסימני פיסוק כמובן

  • בגדול, בתכנות, יש הבדל בין סוגי משתנים: constant הוא משתנה שמחזיק ערך קבוע, variable אמיתי הוא משתנה שמחזיק ערך שמשתנה לפעמים גם כאן, באש הוא צ'יל ולא מבקש מאיתנו להגדיר. הקונבנציה היא להשתמש בשמות עם אותיות uppercase בשביל קבועים, ולהשתמש בשמות עם אותיות lowercase עבור משתנים אמיתיים

  • משתנים יכולים להכיל זה את זה! TITLE="text $HOSTNAME"

  • יש לנו אופציה לעטוף שם של משתנה בקוד בסוגריים מסולסלות כדי לנתק אותו מהקונטקסט שלו אם אנחנו רוצים לקרוא לקובץ בשם של המשתנה x עם הספרה 1 בסוף, כלומר x1, לא נוכל לעשות touch $x1, זה ידפיס לנו את המשתנה x1 שהוא ריק/לא קיים; נעשה במקום touch ${x}1 וזה יעבוד אז לשים לב שהדולר בא לפני הסוגריים - כמו תמיד עם משתנים, כדי להגיד "הערך של..."

local variables, local

  • הם משתנים שתקפים רק ל-Shell function מסוימת בקוד, ולא לכל התוכנה. זה נוח כדי ליצור הפרדה ולהימנע מחפיפה בשמות.
  • הפקודה local מגדירה אותם, ככה:

    foo=1
    funct_1 () {
    local foo=2
    echo "$foo"
    }
    funct_1
    echo $foo
    

  • בדוגמה הזו, האאוטפוט של funct1 יהיה 2, בעוד שהאאוטפוט של ה-echo בשורה האחרונה יהיה 1

  • עוד [[Bash Scripting#shell functions|בקובץ הסקריפטינג]]

variable before command

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

    IFS=":" read P1 P2 P3 P4 <<< "/etc/psswd" (עושים לקובץ שמפריד ערכים עם : רידיירקט לתוך read כדי שתקבל ממנו ארבעה ערכים.)

    בגלל שהגדרת המשתנה קורית באותה שורה עם הפקודה read (באופן שלא היה תקין עם כל פקודה אחרת: הן הרי אמורות להיות מופרדות בשורה, או מחברות עם אופרטור) -- ה-shell יודע שזו הגדרה זמנית שלו, ולאחר הקריאה של הקובץ, IFS יחזור לערך הקודם שלו.

  • הצורה הכללית: VAR="VALUE" <command> <command-arguments/options>

declare

  • אמנם לא חייב, אבל אפשר להכריז על משתנים כדי להגדיר אותם קצת (בקושי משתמשים בזה) declare -r VAR=value יעשה שהמשתנה שלנו הוא read only, ואז הוא בעצם constant... (קבוע) declare -i יעשה את המשתנה שלנו integer (מספר) בלבד

  • הפקודות declare -u ו-declare -l משמשות ל-Case Conversion כלומר, כדי להמיר אותיות ב-Uppercase ל-Lowercase ולהיפך. אם 'הצגנו' משתנה באמצעות אחת מהפקודות האלה, לא משנה איזה ערך הוא יקבל, הוא ינרמל אותו לאפרקייס/לוארקייס בהתאם.

  • אפשר לעשות declare -a name כדי ליצור מערך בשם name

environment variables

המשתנים הסביבתיים הם משתנים שה-shell מגדיר מחדש באופן יחסי בכל מערכת שהוא רץ עליה, והם מאפשרים לו "מגע עם הסביבה שלו". הם מוגדרים מראש עבור כל סשן ב-bashrc, ואפשר להכריז עליהם ספונטנית עבור סשן מסוים שרץ.

המבנה הכללי שלהם הוא key=value printenv - להראות את המשתנים הקיימים אפשר לעשות printenv VARIABLE בשביל ערך של משתנה ספציפי (אפשר כמובן גם echo $VAR)

  • אפשר להריץ תוכנה עם ערך ייחודי לאחד המשתנים הסביבתיים
    • למשל, הרוחב של שורה ב-man נקבע ע"י משתנה סביבתי בשם MANWIDTH
    • כדי לשנות אותו בהרצה מסוימת של man נעשה MANWIDTH=75 man command

declare

והדרך להכריז עליהם עבור סשן בודד היא declare -x <KEY>=<VALUE כדי לבטל משתנה סביבתי נעשה עם פלוס על האיקס: declare +x <KEY> וכדי לקבל רשימה של כל המשתנים הסביבתיים שאנחנו הגדרנו אפשר לעשות גם declare [-p] לא יודע במה זה עדיף על printenv

אפשר לעבוד עם גם export, הנוהג הוא שזו הפקודה לשים בקובץ bashrc

export

export <KEY>=<VALUE> כדי להגדיר משתנה סביבתי export -n <KEY> כדי לבטל export [-p] כדי לקבל רשימה שלהם

חידוד: יש גם export PATH, פשוט אומר שאנחנו רוצים שהתוכן של PATH יהיה זמין ל-child processes של הטרמינל שבו הפקודה רצה. לכן, אם למשל אנחנו עושים export ~/exp אנחנו בעצם אומרים "מעכשיו כל מה שהטרמינל הזה מריץ יודע לקרוא מ-PATH".

special variables

  • הם לא משתנים סביבתיים, כי הם לא משתנים קבועים שה-shell טוען בכל פעם שהוא רץ. הם משתנים מסשן לסשן. דוגמה טובה היא BASH_VERSION שיתן לנו את הגרסה המתאימה לסשן בכל פעם. עוד שימושיים: $$ מחזיר את ה-PID של התהליך שהריץ אותו $RANDOM יחזיר מספר אקראי, אבל מתוך טווח די קטן במונחים של מחשב.

useful environment variables

![[Pasted image 20250124235558.png|500]] $HOME - ~/
זו תמיד התיקיה ש-cd בלי argument יוביל אליה $pwd - path to working directory - $GLOBIGNORE מכיל רשימה של פאטרנים שבאש מתעלם מקבצים שמתאימים להם בשם $path זה כמובן איפה שבאש מחפש פקודות $EXECIGNOREמחריג תבניות של פיילניימז מלהיספר כפקודות בתיקיות path $PPID פרוסס איידי של כל השלז $BASHPID פרוסס איידי של הטרמינל הנוכחי בלבד

בכל מקרה, בכל סוגי המשתנים, **בכל מקרה, בכל סוגי המשתנים, לא מכריזים עם $! כותבים $varname (עם הדולר בהתחלה) רק כשרוצים לרפרר לערך של משתנה שכבר הוגדר חידודים ספציפיים:

PWD

pwd זהה ל-echo $pwd אבל שונים מ-bin/pwd הראשונים רצים עם -L בברירת מחדל השני רץ עם -P בכל אופן אפשר לתת את הארגומנט השני Options:-Lprint the value of$PWDif it names the current working directory-P` print the physical directory, without any symbolic links

source

  • משמשת כדי להריץ סקריפט ב-Shell Session הנוכחי כשאנחנו מריצים סקריפט, באש פותח Subshell חדש ונותן לו את הסקריפט כאינפוט. אם אנחנו רוצים לשחק עם משתנים של הסשן הנוכחי, או לעשות כל דבר שדורש מגע עם הסשן הנוכחי שלנו, הפקודה source בעצם מזינה את הסקריפט כאינפוט לסשן הנוכחי

  • אם רוצים לטעון מחדש קבצי מערכת, יש reload path אבל יש גם source path למשל source ~/.bashrc

  • במקום לכתוב source אפשר גם פשוט לעשות . (נקודה)

לזכור: כשאנחנו "טוענים" תוכנות, ה-shell פשוט מקבלת את הקוד שלהן כאינפוט, כאילו כתבנו לה את כל הפקודות שבקובץ מיד ברגע שהפעלנו את ה-shell...

set

  • משמשת להגדרה של משתנים המייצגים shell options או positional parameters מפעילה הגדרות שונות שיפעלו ב-shell session באופן גורף, כלומר, בסקריפט הן יפעלו לכל אורך הסקריפט. רובן קשורות לדיבאגינג ואאוטפוטים. להסתכל ב-(קישור לכתיבה מתגוננת).

  • יש הדורשים להשתמש ב-e, u ו-o pipefall ביחד, אבל כיום הרוב בדעה שאין סיבה להשמש בהן בכלל - הסיבה היא שיש פקודות שצריכות להחזיר exit non 0 בכל מיני מצבים, אז כדי שה-setים יפעלו, היה צורך ליישם כל מיני חוקים מיוחדים (מתי הכישלון "נחשב" ומתי לא) - החוקים האלה לא עקביים ולא אפקטיביים באיתור באגים אמתיים. (הדברים האלה קיימים בבאש כי זו דרישה של POSIX במטרה לקדם קוד יציב).

set -e

  • יפסיק את הסקריפט אם כל פקודה שהיא מחזירה non zero exit status, חוץ מכמה יוצאי דופן הכרחיים (?)

set -u

  • יעצור את הסקריפט ברגע שמופיע משתנה שלא הוכרז לפני כן (קיבל ערך)

set -o PIPEFAIL

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

set -o vi

מפעיל Vim Keys בטרמינל אפשר להוסיף את set -o vi ל-bashrc כדי שיפעל תמיד ברירת המחדל היא דווקא מקשי emacs, והפקודה היא set -o emacs

alias

alias name='string' כדי ליצור 'פקודה' חדשה, למשלalias rclonemount = 'rclone mount gdrive:/ ~/bigbro'

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

אם יש משהו שאנחנו משתמשים בו עם הגדרה מסוימת לעתים קרובות, אפשר להשתמש ב-alias למשל alias ls='ls -la'

date

פקודה שמשמשת כדי להדפיס את התאריך... המבנה הוא date +'specifiers' כשאלה הן האפשרויות (יש עוד): - לשים לב שיש רווח רק לפני הפלוס - %Y → Full year (e.g., 2025) - %m → Month (e.g., 01 for January) - %d → Day (e.g., 31) - %H → Hour (24-hour format, e.g., 14) - %M → Minutes (e.g., 45) - %S → Seconds (e.g., 59)

  • כשאנחנו רוצים את התאריך ב-string, נרצה את הערך של הפקודה כ-string, ולא את הפקודה עצמה, אז נעשה (למשל) ככה: $(date + '%Y-%m-%d')

basename

  • משתנה שעובד דומה ל-date, יודע לקחת את השם של קובץ בלי ה-Path שלו $(basename path)

מערכים


בסיס: - משתנים שמחזיקים ערך יחיד נקראים scalar variables, בעוד שמערך (array) הוא משתנה שמחזיק ריבוי של ערכים מערכים הם חלק מהותי בכל שפת תכנות, והתמיכה של ה-shell בהם היא די מינימלית.

  • בגדול, מערך הוא כמו קובץ אקסל כל תא בו מכונה element, והוא מכיל ערך יחיד למיקום שלו במערך נקרא index או subscript כמות ה-'צירים' שיהיו לנו בטבלה נקראים dimensions - ה-'מימדים' של הטבלה (זה עובד כמו ב-'מימדים' של גרף: בקובץ אקסל יש לנו 2 'מימדים': גובה ורוחב; מערך תלת-מימדי יכלול גם 'ציר z'.) ה-shell תומכת רק במערכים חד-מימדיים, כלומר, בשורה/טור יחידים של ערכים, בלי שזו תהיה 'טבלת אקסל' של ממש. (פשוט רצף כלשהו של ערכים). מערכים באופן כללי הם תכונה של bash 2.0 והם לא תואמים ל-narrow posix shell.

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

יצירה ושימוש:

  • כדי ליצור מערך יש לנו שתי דרכים:

    1. לתת לו ערך ולהכריז עליו באותו זמן: a[1]=something אומר "האיבר הראשון במערך a שווה ל-something" לסימון [1] קוראים גם ה-subscript
    2. לעשות לו declare -a הפקודה declare -a a תיצור מערך בשם a
  • כדי לתת ערך למערך שוב יש כמה דרכים:

  • לתת ערך יחיד: באמצעות הסינטקס arrayname[subscript]=value אפשר לתת לאיבר בודד במערך ערך יחיד. יש לשים לב שהאיבר הראשון במערך הוא איבר מס' 0 ולא מס' 1.
  • לתת מספר ערכים: באמצעות הסינטקס arrayname=(value1 value2 value3 value4 value5) אפשר לתת למערך מס' דינמי של ערכים - וכל אחד ישוייך לאיבר נפרד במערך (כאשר הראשון ילך ל-0, השני ל-1, וכן הלאה)

    (כלומר, אם משתנה מקבל כמה איברים מופרדים ברווח, בתוך סוגריים עגולות - הוא מערך!)

אפשר גם להחליט איזה ערך הולך לאיזה איבר באמצעות סינטקס כזה: arrayname=([0]=value1 [2]=value2 [1]=value3)

  • ליצור את איבר 100 לא יצור את 0-99 בשפות תכנות רבות, אם נגדיר את האיבר ה-100 במערך (לדוגמה), איברים 0-99 יווצרו אוטומטית כמשתנים ריקים, וניתן יהיה להתייחס אליהם בקוד (will be initialized). זה לא ככה ב-bash, ואם נגדיר רק את array[100], איברים 0-99 לא קיימים במערך.

פעולות על מערכים:

  • אפשר לבצע כל מיני פעולות ברמת המערך - החל מפעולות 'פילטר' (כמו באקסל) ועד לשינוי הערכים.
  • ה-subsets [@] ו- [*] מתייחסים לכל האיברים ביחד כשהם מוצמדים לשם המערך הבדל ביניהם הוא כמו ההבדל בין *$ ו-@$ עבור positional parameters: כלומר: ללא גרשיים שניהם יתנו לנו רשימה של כל מילה במערך שלנו בנפרד (כך שאחרי כל רווח תבוא שבירת שורה - גם אם זה רווח בתוך איבר ש-'תחום' ב-"") אך כשהם עטופים בגרשיים " הכוכבית תתן לנו רשימה 'שטוחה' של כל המילים מכל הערכים, בעוד שהשטרודל יתן שורה נפרדת לכל איבר שתחום ב-"" כמעט תמיד נעדיף את @ כדי לשמור על המשתנים שלנו מלהתערבב קצת קשה להסביר את זה בכתב, הנה תמונה: |500

  • הקידומת # תתן לנו expansion של מס' האיברים במערך (כמו שהיא תתן את מס' האותיות עבור string רגילה) ${#a[@]} זה הסינטקס ${#a[100]} בעוד שזה, כידוע, יתן את האורך של האיבר המאה באותיות

  • כדי לבדוק אילו איברים קיימים במערך: (כזכור, ליצור את 100 לא יוצר את 0-99 אוטומטית, לכן יכולים להיות 'רווחים' במערך:) נשתמש באחד מאלה: ${!array[*]} ${!array[@]} כאשר ההבדל בין @ ל-* הוא אותו דבר כמו עבור # (כמו גם בפרמטרים מיקומיים, נעדיף לרוב את @).

  • כדי לעשות append (לספח) למערך:
    נשתמש ב-=+ נניח שעשינו כבר array=(a b c) ואנחנו רוצים להוסיף איברים 4-6 שהם e d f. נעשה: array+=(d e f)


:'slice' - סינון מערכים

  • יש צורה מיוחדת של parameter expansion שלמעשה מאפשרת להדפיס טווח מסוים של איברים מתוך המערך הסינטקס קצת מורכב:
    arr=(0th 1st 2nd 3rd 4th)
    echo "${arr[@]:2:3}"
    ---
    2nd 3rd 4th
    

הסבר: הסינטקס :2:3 אומר "תדפיס שלושה איברים החל מהאיבר השני" (התצוגה מתהפכת כאן, הסדר הנכון הוא זה שרואים בקודבלוק)

  • אפשר להשתמש במינוס בערך הפרמטר כדי למספר את האיברים מהסוף להתחלה:
    [me@linuxbox ~]$ echo "${arr[@]: -2:2}"
    3rd 4th
    

הסבר: הלכנו לאיבר השני מהסוף, וממנו ספרנו 2 קדימה, כך שלמעשה חילצנו את שני האיברים האחרונים. (פעמיים מינוס שתיים היה סופר מהשני מהסוף, וגם סופר לכיוון הנגדי). חשוב לזכור שהספירה "כוללת את האיבר", במובן שאם הלכנו ל-3rd בדוגמה למעלה, וספרנו ממנו 2 קדימה, למעשה ספרנו אותו + את 4th.

לשים לב שמשום מה צריך רווח לפני שמשתמשים במינוס! (לא הבנתי אם זה רק כשמשתמשים אחרי ה-prompt, או שיש צורך לשים רווח לפני כל שימוש במינוס.)

  • אפשר להשתמש בהתרחבות הזו כדי ליצור מערכים חדשים: arr2=("${arr[@]:2:3}")

סידור מערכים עם sort

  • אין דרך מובנית לעשות sort לאיברים של מערך, צריך לעשות להם echo ולהריץ על האאוטפוט sort...
a_sorted=($(for i in "${a[@]}"; do echo "$i"; done | sort)) echo "Sorted array: " "${a_sorted[@]}"

הסבר: נתון מערך a; על מנת לסדר אותו ולשמור כמערך חדש בשם a sorted, יצרנו לולאת for עבור @ של a (כל האיברים, מופרדים כמילה/שורה לפרמטר) - הרצנו echo עבור כל אינפוט וניתבנו את הכל ל-sort באמצעות pipe.


דרכי שימוש נפוצות: - התאמה בין לולאת for למערך:

for i in {0..23}; do hours[i]=0; done

שורה כזו בעצם יוצרת משתנה i עבור כל מס' בין 0 ל-23, ואז עבור כל משתנה כזה, יוצרת איבר מתאים במערך hours.

  • שילוב עם פקודת read בד"כ נשתמש ב-read באופן כזה: while read line -r כאשר בכל איטרציה של הלולאה, read מגדירה את line. דרך נוספת, עם שימושים אחרים, היא לתת ל-read את הדגל -a, שאומר "כל אינפוט שתקרא מקבל איבר משלו במערך": read -a array <<< "value1 value2 value3"

mapfile

  • פקודה חדשה יחסית שקוראת STDIN ישירות לתוך מערך mapfile -options arrayname אפשרויות: mapfile -t -n 32767 words < "$WORDLIST" לדוגמה ייצור מערך בן 32767 איברים בשם 'words' עם הערכים שיקבל

unset

  • פקודה שמשמשת למחיקת מערכים: לתת למערך ערך ריק לא מאפס את האיברים שלו! arrayname= לא עובד, כי כל התייחסות למערך בלי פרמטר היא בגדר התייחסות לאיבר 0 (ראשון) של הפרמטר. (=blank פשוט יאפס את איבר 0 בלבד).

unset <arrayname>

  • ניתן למחוק גם איברים בודדים: `unset '[subscript]'

לשים לב שחייבים '...' כדי שערך האיבר לא יתרחב ב-shell!

Associative arrays מערכים אסוציאטיביים

  • החל מבאש 4.0, ניתן ליצור מערכים שהאינדקסים שלהם אינם מספרים אלא strings כלומר, במקום שיהיו לנו איברים שממוספרים ב: 1,2,3 וכו', יהיו לנו סטרינגס שהם למעשה 'שמות התאים' של האיברים השונים. זה מבנה נתונים שנקרא 'key-value' ונטען בחוברת שהוא בנוי כמו מילון (? לחשוב על זה)

  • כדי ליצור מערך אסוציאטיבי, אי אפשר פשוט לתת לו ערך כמו למערך רגיל, אלא יש צורך להכריז עליו:

    declare -A colorscolors["red"]="#ff0000"colors["green"]="#00ff00"colors["blue"]="#0000ff"
    

לשים לב שזה הדגל -A ולא -a! אות A גדולה = מערך אסוציאטיבי השימוש באיברים במערך כזה הוא בדיוק כמו השימוש באיברים במערך רגיל: echo ${colors["blue"]}

  • אפשר להשתמש במערכים אסויצאיטיביים כדי "לזייף" מערכים מרובי מימדים: נגדיר שני טווחים, נניח x ו-y, ונגדיר משתנה address שהוא "x, y" עבור כל ערך אפשרי של x ושל y. כל משתנה כזה הוא "משבצת", ונוכל למספר אותן ע"י הזנת המשתנים שיצרנו לתוך מערך מסוג [multi_array[$address. כך, נקבל טבלה ממוספרת, ונוכל לעבוד על המשבצות שלה לפי המספור שלהן בטבלה. או שאפשר לעשות for (( var; var; var )).

רידירקציה, לולאות, סקריפטים

[[Bash Scripting]]

כל פקודת הרצה מתייחסת ל-exit status (הצלחה\כישלון...) של הפקודה שקודמת לה מידית!

&, &&, line break:

  • & אומר "להמשיך מיד לפקודה הבאה" וזו הסיבה שאם הוא מגיע בסוף פקודה או סקריפט, הוא משחרר את הטרמינל

  • && אומר "להמשיך לפקודה הבאה ברגע שהקודמת סיימה לרוץ, ובתנאי שהיא הצליחה", לשים לב שלא ניתן להתקדם מפקודות שממשיכות לרוץ ברקע (rclone mount למשל) - הן יתקעו אותנו אם נשים &&

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

  • אם נריץ פקודה עם & בסוף ככה שהיא ממשיכה לרוץ ב-background, נקבל אאוטפוט בסגנון הבא: [1] XXXXX זו בעצם הדרך של הטרמינל להגיד: התחלנו job מס' [n] וה-PID שלו הוא xxxx (אפשר לראות את זה ב-ps) אפשר לראות את כל ה-jobs עם jobs

semicolon ;

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

double pipe ||

command || עובד הפוך מ &&, ממשיך הלאה רק אם הפקודה נכשלת

Grouping Brackets {} ()

curly brackets {}

{ command1; command2; command3; } :הסינטקס הרגיל

  • הופך את כל הפקודות שבסוגריים (מופרדות כרגיל ברווחים) ל-Group Command - פריט יחיד בכל הנוגע ל-Redirection
  • יש להפריד עם ; כדי להגיד ל-shell שלא מדובר בארגומנטים אלא בפקודות נוספות... { command-a & command-b} הופך את הפקודות לבלוק אחד מבחינת סטטוס יציאה. המשמעות היא שלשים && אחרי הבלוק הזה יתנה את ההמשך בהצלחה של שתי הפקודות, וכן הלאה.

  • [[Bash Scripting#shell functions|פקודות של פונקציות]] הן תמיד בתוך סוגריים כאלה

  • עדיף על סוגריים עגולות במקרה בו אנחנו לא צריכים subshell! צורך פחות זיכרון.

(...) normal / single round brackets

  • דומה ל-{}, די שקול ברוב המקרים, אבל יש הבדל: הסוגריים המעוגלות יוצרות Subshell שמריץ את הפקודות שבתוכן ב-shell session נפרד. הסוגריים המסולסלות מריצות את הפקודות כ-group בתוך ה-shell session הנוכחי

    אם נעשה: echo $(cd ~/ && ls) && cd נקבל את ה-ls של home, ואז את ה-cd שהיינו עליו ב-shell. זה בגלל שה-cd הראשון שוגר ב-session נפרד (של הסוגריים), ולא השפיע לנו על ה-cwd בסשן הראשי.

    בעוד שאם נעשה: echo ${ cd ~/; ls; } && cd זה ישפיע לנו על ה-cd בסשן הראשי, ובהכרח נקבל פעמיים את הפאת' של home

  • באופן כללי חיבור פקודות זה שימושי מאותן סיבות כמו בלוגיקה ובמתמטיקה... אם עשיתי: if [[ ! (command1 && command2) ]] אז ! יהפוך את ה-exit status של הקוניונקציה של command 1 ו-2 אבל אם עשיתי: if [[ ! command1 && command2 ]]' אז בעצם נמשיך ל-command 2 רק במקרה ש-command 1 נכשלה, כי!` פועל רק עליה, והופך בין 0 ל-1 ב-exit status

  • להשתמש ב-Groupings חכם זה לעשות: { command-a; && commandb; } && { command-a; && commandb; } וכו'... כדי ליצור היררכיה של 'התניות' באמצעות קוניונקציות, או גם פייפים! ||

parenthesis 'x', "x"

[[מילון BASH#פקודה בודדה באמצעות ssh#|לקוח מתוך כאן]]

יש לזכור את העיקרון - 'command' בגרשיים עוברת כ-string ולא כפקודה (ללא expansion). אבל ה-string היא ללא הגרשיים - ואז הפקודה הופכת ל-command. - אז אם אנחנו צריכים להעביר פקודות להוסט, נעטוף אותן בגרשיים כדי שיקראו כ-string ע"י המערכת המקומית בשלב הראשון, ואז יעברו כפקודות בלי גרשיים כאינפוט להוסט. ssh remote-name 'ls * > dirlist.txt' יעביר את הפלט של ה-ls לקובץ dirlist *ב-root של ה-host, כי הפקודה עברה במכונה כסטרינג, ואז אל ההוסט בלי הגרשיים,, כלומר כ-command!

### backslash \ בסוף שורה אומר לבאש להתעלם מה-Line break command \ && command 2

#! shebang

  • כשיש לנו #! זו לא הערה אלא שיבנג. הוא אומר לקרנל (למה לקרנל ולא ל-shell?) עם איזה interpreter הוא קורא את הסקריפט. #!/bin/bash הוא המיקום של השל שלנו, באש...

    חשוב להבדיל מ-#!/bin/sh שהוא המיקום של ה-symlink ל-default shell שלנו. בד"כ הוא מקושר לבאש, אבל לפעמים הוא מקושר ל-POSIX Shell/Narrow Shell

PS2 Shell Prompt ">"

  • כשאנחנו מקבלים את STDOUT בטרמינל, בכל שורה חדשה יהיה "<" בהתחלה
  • זה בעצם prompt (טקסט מקדים) שמוצמד לכל פלט של PS2 (המשתנה שמכיל את STDOUT)
  • השימושיות תתברר בהמשך

read

בגדול read היא הדרך שלנו לעבד STDIN. פקודה שיכולה לקבל או argument ו-option או קובץ/פייפ ו-option בד"כ תבדוק אם יש ערך ל-$1, ואם אין, נעשה read למטרה שמגיעה מ-STDIN.

הרבה פעמים משתמשים ב-command | while read line כדי לעבד שורות מאאוטפוט, אחת אחרי השניה; זה בגלל שכשהיא מקבלת אינפוט, read פועלת על כל שורה בנפרד (כשנגמרות - מחזירה 1) (כמו שלמדנו, הגדרנו רק את משתנה line, אז הוא יקבל את כל המילים - וכל שורה היא "אינפוט" נפרד! )

מקבל אינפוט מהמשתמש דרך הטרמינל, או מקובץ, ושומר אותו במשתנה או במשתנים. read var1 var2 var3' מגדיר את המשתנים ובאינפוט, כל מילה הולכת למשתנה לפי הסדר: hello I'm Tony = var1(hello), var2(I'm), var3(Tony) אם יש יותר מילים ממשתנים - כל המילים העודפות נשמרות על המשתנה האחרון ואם אין ערך למשתנה - הוא מקבל "" (blank) משתמשים ב-$ כדי לרפרר למשתנה, כמו תמיד...

echo "Please enter your name: "
read name
echo "Hello, $name"

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

read -p "Enter your name: " name
echo "Hello, $name"

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

echo "Enter three numbers separated by space: "
read -a numbers
echo "The numbers are: ${numbers[0]}, ${numbers[1]}, ${numbers[2]}"

  • אפשר להשתמש בדגל -s כדי לקבל אינפוט "שקט" (שלא יודפס על המסך, כמו כשמקלידים ססמה ל-sudo)
  • אפשר להשתמש בדגלים-e ו- -i כדי להגדיר ברירת מחדל במקרה שהיוזר לא נותן ערך חדש read -p "Enter your name: " -i "John Doe" name אפשר להשתמש בדגל -r כדי להדפיס באקסלאשים כרגיל! כברירת מחדל, read דווקא מתייחס ל-escape characters (לעומת [[מילון BASH#echo#|echo]] שפועל הפוך) יש גם -t בשביל טיימר, -A בשביל מערך אסוציאטיבי (??) ועוד, לא נשמעים הכי שימושיים, אפשר לגגל אם צריך

אם לא הוגדר שום משתנה ל-read, הכל נשמר אוטומטית על המשתנה $REPLY

control structures\control flow statements

dos2unix

  • כשכותבים סקריפט בתוכנת ווינדוז, למשל VS CODE, אנחנו מקבלים carriage return character שונה מב-unix; לכן, יש צורך לפרמט מחדש את הקובץ. dos2unix filename כדי לפרמט מחדש

function, return

  • הפקודה function פותחת הגדרה של Shell Function והפקודה return סוגרת פונקציה כדי לסיים את הפקודות שלה ולחזור לשורה שבה היא קיבלה call (אחת אחריה ליתר דיוק)
  • מתועד יותר באריכות [[Bash Scripting#shell functions|בקובץ הסקריפטינג]]
  • חשוב לזכור ש-return מחזירה אותנו לאיפה שה-call התבצע!!!

conditionals

if, then, else

  • מיועד כמובן כדי ליצור branchים בתוכנה (אם קורה א' תעשה ככה, אם קורה ב' תעשה ככה) if argument-command; thencommand2 else command3 fi

  • מה שמעניין ב-bash זה שגם התנאים של הלולאה, מה שמופיע ב-argument command, הם פקודה. איך מתנים פקודות בפקודה? זה קל למי שמקשיב: לכל פקודה יש [[מילון BASH#exit status|exit status]] שנוצר עם סיום ההרצה שלה, בהתאם להצלחה או לכישלון שלה; פקודת if אומרת למעשה "אם הערך של הפקודה בסוגריים הוא true, ו-else אומרת "אם הערך שלה הוא false".

  • אם סיפקנו מספר פקודות - ההתקדמות של if תהיה לפי ה-exit status של האחרונה ביניהן

  • Test conditions (like file or string checks): if [ ... ]; then
  • Commands that return an exit status: if command; then

שימוש בפקודה כהתניה בפועל: - אם שמנו משהו כמו grep נגיד בהתניה של if - היא תחזיר true אם יש לה ערכים בסוף (אם הייתה התאמה אחת לפחות), ובמקרה אחר לא (סטטוס יציאה 1+). זה נכון להמון פקודות.

היפוך הערך של ההתניה - כדי לקבל ערך "TRUE" רק אם ה-exit status הוא דווקא 1+ (FALSE), אפשר להשתמש בפקודת ה-if ישירות (לא לכל פקודה יש מקבילה ל-grep -v "שהופכת" את האאוטפוט).

    `if ! echo "$var1" | grep "^hello$"; then` 
הסיפה של if תופעל כאן אך ורק אם **אין** התאמה מדויקת ל-hello. בגלל שיש !

case

  • לולאה לא בסיסית, אבל חשובה
  • בהרבה מקרים, יש לנו בחירה בין כמה אפשרויות עבור תוכנה, ואם היינו בונים אותה עם if, היינו צריכים לעשות מספר "תאים" של if else if

  • במקום זה, case היא 'multiple choice compound' - היא מאפשרת בחירה בין כמה אפשרויות, באופן הבא:

    case <string-or-var> in
    value) commands
    more commands
    ;;
    value) commands
    more commands
    ;;
    value) commands
    more commands
    ;;
    esac
    

  • אם אין לנו כמה שורות של פקודות, נעשה: value) commands ;;

  • ה-case תחפש את הערך של <string-or-var> לפני אחת מהסוגריים - ואם יש התאמה, תתריץ את כל הפקודות החל מאחרי הסוגר ועד ל-;; ה-esac הוא פשוט ההפך מ-case... אנחנו בעצם בונים "רשימה", יחסית מסודרת ויזואלית, של אפשרויות שונות להתאמה, ופקודות שהן יריצו.

  • הסוגר מסמל את הסוף של ה-pattern שנתאים כנגדה אפשרויות ל-pattern:

  • כמו שניתן לראות בתמונה, גלובים עובדים ב-case
  • בנוסף לזה, אפשר להשתמש ב-| (סימן של פייפ, אבל ב-case זה לא פייפ -) כדי להגיד "או pattern-a, או pattern-b" (וכך הלאה): q|Q) echo "you typed q" ;; כדי ללכוד גם אות גדולה וגם קטנה, למשל.

  • עד באש 4.0, case ידעה למצוא התאמה אחת בלבד פר אינפוט מאז 4.0, אפשר להשתמש ב-;;& במקום ב-;; רגיל כדי להגיד לבאש: "בדוק את ההתאמה הזו, בצע את הפקודות אם היא מתקיימת, ואז תמשיך לבדוק את ההתאמה הבאה"

    המשמעות היא שכל טסט שנסיים ב-;;& הוא לא אקסלוסיבי ויאפשר לבדוק את האינפוט כנגד עוד תבניות (case שכולו כאלה הוא פשוט case שמתאים את כל מה שאפשר)

test, [ ... ] , [[ ... ]] (square brackets), (( ... )) (double rounded brackets)

test & [ ... ]

  • הפקודה test שקולה לפקודה [ ... ]
  • הפקודה הכי נפוצה יחד עם if וכו' - מאוד שימושית
  • היא בעצם נועדה לתת ללולאות התניות "רגילות" יותר מפקודות - כלומר פשוט לבדוק את ערך האמת של ביטוי (במקום exit status של פקודה "אמתית") - כאשר ה-exit status של test יהיה בהתאם לערך האמת הזה. עושים test expression או פשוט: [ expression ] לשים לב שיש רווח מכל צד של הסוגריים המרובעות.
  • מעניין לדעת שזו פקודה מובנית ב-bash, אבל היא קיימת גם כתוכנה ב-/usr/bin כדי שנוכל להשתמש בה עם shells נוספים המבנה הלוגי של התוכנה מעניין: ] היא הפקודה... היא מקבלת ארגומנטים נוספים לאחר מכן, זו הסיבה שיש לנו רווחים בשולי הסוגריים, והיא גם דורשת ש-] תהיה הארגומנט האחרון שלה (הסוגריים מתהפכות כאן, הכוונה היא כמובן לפתיחה בהתחלה ולסגירה בסוף).

  • ה-test תומכת במגוון רחב של expressions בתוך הסוגריים, הנה רשימה חלקית, יש עוד ב-man page

תכונות של קבצים: ![[Pasted image 20250202230402.png|500]]

השוואות בין strings: ![[Pasted image 20250202234055.png|500]]

השוואות בין מספרים:

![[Pasted image 20250202234333.png|500]]

[[ ... ]] (Double Brackets)

  • גרסה מודרנית יותר של test ושל [ ... ] שתומכת ב-Regex (זו דרך לעשות Regex בתוך if למשל)

  • אפשר לשים בסוגריים את כל הסינטקס הרגיל של test, ובנוסף אפשר להשתמש ב- =~ (קשה להבין פה: זה סימן שווה קודם ואחריו הסלסול.)

  • האופרטור =~ משווה בין string שניתן לו לבין regular expression שיבוא אחרי האופרטור מדובר ב-ERE, והפקודה מחזירה 0 (true) אם יש התאמה [[ "hello" =~ 'ello' ]]יחזיר 0 - true

  • בנוסף, רק [[ ]] יודעת לתמוך ב-pathname expansion כלומר שרק איתה אפשר לעשות: if [[ $FILE == foo.* ]]

  • בהמשך לנקודה האחרונה, בסוגריים מרובעות-כפולות ועגולות-כפולות לא צריך לעשות escape characters זה בגלל ש-[[...]] ו-((...)) הן תוכנות shell שייחודיות לבאש בעוד שה-test הקלסי הוא חלק מהסינטקס של כל POSIX Shell/Narrow Shell (הם command arguments)

    לדעתי זה הופך את הכתיבה ליותר קלה שאין אסקייפים...

(( ... )) Double Round Integer Brackets

  • עוד תוספת של bash ל- test & [ ... ] הקלסיים
  • נועדו לבצע בדיקות של ערך אמת על אינטגרים (מספרים)
  • האופרטורים שמשתמשים בהם הם כאלה שמחזירים ערך אמת: >, <, == ועוד כמה
  • בתוך סוגריים כפולות, לא חייב לתת $ לפני שם של משתנה זה בגלל שאותיות לא קיימות בתוך הסוגריים גם ככה, אז הסוגריים תמיד יבדקו אותן כמשתנה

אופרטורים לשילוב ביטויים

  • כל ה-Compounds שתחת הכותרת הזו (test וכל הסוגריים) מאפשרים לחבר ביטויים באמצעות שלושה אופרטורים: AND, OR, NOT

  • ב-test ו-[ ... ] כותבים אותם: -a AND -o OR ! NOT

  • ב- [[ ... ]] ו-(( ... )) כותבים אותם: && AND || OR ! NOT

Loops

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

while, until


While:

# count from 1-5 and print each step
variable=1
while [[ "$count" -le 5 ]]; do
echo "$count"
count=$(( count + 1 ))
done
echo "Finished"
  • הסינטקס הכללי הוא:

    while <commands>; do
    commands 
    done
    
    - (אבל לזכור ש-; שקול לשבירת שורה, אז אפשר לפרמט את השורות איך שרוצים, כל עוד מוסיפים ; בהתאם)

  • עובד דומה ל-if - הפקודות ימשיכו לרוץ כל עוד סטטוס היציאה של התנאי הוא 0 (True) בדוגמה שלמעלה, המשתנה count מגיע לערך 6 והתנאי של while מתחיל להחזיר 1 (False). לכן הסקריפט עובד.

- לאחר שהתנאי מפסיק להתקיים, הקוד ממשיך מהשורה שאחרי done

Until - נכתבת בדיוק כמו while, ועושה את ההפך ממנה כלומר, until מריצה את הפקודות שלה עד שהתנאי מחזיר 0 (True) בדוגמה שלמעלה אפשר היה להשתמש ב-until ולבדוק האם המשתנה גדול מ-5, במקום להשתמש ב-while ולבדוק אם הוא קטן/שווה ל-6 הבחירה בין השתיים לרוב קשורה לשיקולים של בהירות - מה שיותר אינטואיטיבי.


רידירקציה ללופ

  • לולאות מקבלות STDIN. אז אפשר לעשות: done < file.txt (רידירקציה בסוף הלולאה) כדי שתבצע את הפקודות בהתניה שלה על STDIN מקובץ.

  • עם > בדוגמה הבאה, הלולאה תפעל כל עוד read פועלת על הקובץ ומחזירה exit status 0. כלומר, היא תפעיל את commands על כל שורה ב-file.txt, עד שיגמרו השורות, ואז היא תחזיר 1 (או משהו אחר שאינו 0), והלולאה תישבר.

    while read ...; do
    commands
    done < file.txt
    

  • עם | (פייפ) בדוגמה הבאה, הלולאה מקבלת STDIN מפייפ, ותפעל כל עוד read מקבלת ערך. כלומר, commands יופעל על כל שורה באאוטפוט של הפייפ לזכור ש-pipe פותח subshell - משתנים שהלולאה תגדיר לא יישמרו בסופה

    cat file.txt | while read...; do
    commands 
    done
    

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

break, continue

  • כדי לצאת מיד מלופ, ולהמשיך לשורה שאחרי סוף הלופ, משתמשים ב-break
  • כדי לדלג על האיטרציה הנוכחית של הלופ (לדלג ע
  • ל כל הפקודות שמתחת), ולקפוץ להתחלה של האיטרציה הבאה (לחזור להתחלה של הלופ) - משתמשים ב-continue

    טכניקה נפוצה היא לבנות תוכנת shell ככה שכולה מתקיימת בתוך לופ, בשביל שהיציאה ממנה תהיה באמצעות הממשק שלה בלבד (באמצעות קוד של התוכנה שמוביל ל-break')

  • while true; do היא הצורה הכי קלה להיכנס ללופ נצחי (לזכור ש-true היא פקודה שמחזירה תמיד סטטוס יציאה 0...)

  • כאשר אנחנו בונים לולאות להרבה אפשרויות שונות, תמיד כדאי לשים בסוף הפקודה של כל אחת מהאפשרויות return, פשוט כדי שבאש אפילו לא יקרא את המשך הבדיקה, ויעבור מיד להמשך הסקריפט. (ובאופן כללי, return מאפשרת לקפוץ לסוף הלולאה, ויכולה לחסוך קריאה של שורות שהופכות למיותרות לאחר תנאי כלשהו)

for

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

  • הבסיסית:
    for variable in <...>; do 
    <commands>
    done
    
  • כאשר variable הוא משתנה שיקבל את הערך של הפריט שהלולה פועלת עליו בכל פעם, ו -... כולל רשימה של פריטים.
  • אפשר להשתמש ברשימת הפריטים בטווח {A...D} או בגלובים *.txt התרחבות שנכשלת תחזיר את עצמה כ'מילולית', כלומר שה-* יודפס. לכן כדאי להוסיף קוד שיוודא אם ההתרחבות עבדה.
  • אם לא מספקים ל-for רשימה, ברירת המחדל היא לרוץ על כל positional parameter שקיבל ערך (=על האינפוטים שאחרי הפקודה)
  • בלולאת for שהיא זמנית מאוד, נהוג להשתמש ב-i ואז ב-j,k,l כמשתנים (אם יש for בתוך for) - זה מבוסס על השפה העתיקה Fortan שבה האותיות האלו היו integers כברירת מחדל, לעומת כל השאר שהיו real (סוג אחר של ערך מספרי). (עדיף לתת שם נורמלי, אבל המשתנה של for לרוב לא 'ימשיך הלאה').

  • וצורה מבוססת C: (קיימת בגרסאות חדשות יותר של באש, מבוססת על הסינטקס של הלולאה בשפת C) אם אני מבין נכון - הצורה הזו היא לערכים מספריים בלבד

for (( <conditions>; <end-conditions>; <end-of-loop-commands> )); do
commands
done
- ה-conditions הם פקודות מסוימות שירוצו כשהלולאה מתחילה - ה-end conditions הם ביטויים, שהלולאה תמשיך לרוץ עד שהם יהיו שקריים (F) - ה-end-of-loop commands הן פקודות שירוצו בכל פעם שאיטרציה של הלולאה מסתיימת (נגמרות ה-commands), ולפני האיטרציה הבאה שלה. - הצורה הזו למעשה שקולה לקוד כזה:
<conditions>
while <end-conditions>; do
commands
end-of-loop-commands
done

select

סלקט היא בסה"כ פקודה די חלשה. היא לא חוסכת הרבה הקלדה לעומת לולאה רגילה של while, והיא מגבילה לחלוטין את המראה של התפריט; היתרון היחיד שלה הוא ההדפסה של הפרטים הטכניים ל-error במקום ל-output, כמו שמפורט בנקודה האחרונה.

  • תוכנת shell חביבה ליצירה של תפריטי בחירה התוכנה רצה כ-loop, כלומר מריצה את הבחירה שוב ושוב, עד שאומרים לה break בתנאים מסוימים.

  • סינטקס: select <var> in [string..;] do commands; done <var> הוא שם המשתנה שיחזיק את ערך הבחירה [string..;] היא רשימה של פריטים, מופרדים (בשורות נפרדות או עם דלימיטרים ביניהן) או שהיא ערך פקודה/משתנה, למשל: select option in $(cat file)...

    ; מגיע בסוף הרשימה, לפני do, קצת כמו עם while או if commands הן כל הפקודות שנרצה להריץ לאחר שהבחירה התבצעה (לא לשכוח שצריך ברייק כדי לצאת מהתפריט) done מכריז על סוף הלופ

  • שימוש:

    • האאוטפוט של select יציג את כל אחת מהאפשרויות בשורה נפרדת, עם מספר לפני, ככה: number) string
    • היוזר יחזיר אינפוט של מס' בהתאם לבחירה שלו, וה-string של ה-number שהוא הזין תהיה הערך של $var
  • הפקודה לא שולחת את ערך הבחירה, את האפשרויות או את התפריט עצמו ל-STDIN במקום, היא שולחת אותם ל-STDER (סטרים 2) מה שהיא כן שולחת לאאוטפוט זה את האאוטפוט של הפקודות שהיא מריצה לאחר הבחירה. זה מאפשר לנו לכתוב סקריפט שמתחיל ב-select, ואז לעשות רידירקציה לאאוטפוט המשמעותי שלו, שרץ לאחר הבחירה - ולא לכלול פרטים טריביאליים כמו ההדפסה של התפריט, הבחירה של המשתמש וכו'. script-with-select > outputfile.txt

Positional Parameters

  • כמו שכבר הכרנו, כל משתנה $1, $2 וכו' מחזיק את הערך של האינפוט הראשון, השני וכן הלאה שסקריפט קיבל לאחר השם שלו/הפקודה שלו הם מגיעים עד $9 ואז אפשר לכתוב אותם עם סוגריים מסולסלות: {11}$וכך הלאה אם הם לא קיבלו ארגומנט, הם blank

  • המשתנה $0 תמיד יחזיק את ערך הפקודה הראשונה שהכנסנו לקומנד ליין - כלומר, את הפאת' של התוכנה או הסקריפט שהרצנו

  • המשתנה $# שווה ל- מס' ה-positional parameters שהערך שלהם אינו ריק -- כלומר, הוא תמיד יכיל את מס' הארגומנטים שקיבלנו אחרי הפקודה עצמה (כמה ערכים יש החל מ-$1)

  • אפשר להשתמש ב-positional parameters גם כחלק מפונקציות, כדי שסקריפט יוכל לעשות להן call ביחד עם ערך מסוים, ולהריץ אותן על הערך ($1 יהיה מה שבא אחרי שקראנו לפונקציה, לא $1 שהסקריפט עצמו קיבל) לשים לב ש-$0 דווקא יכלול את ה-path של הסקריפט המקורי שקרא לפונקציה, ולא את שם הפונקציה או את הפאת' שלה (כמו שהיינו מצפים).

  • $* מתרחב לרשימה של כל ערכי הפרמטרים המקומיים, כך שכל אחד הוא מילה נפרדת. אם נקיף אותו בגרשיים, הוא יתרחב לסטרינג מוקפת גרשיים שכוללת את כל ערכי הפרמטרים המיקומיים, מופרדים בתו הראשון של משתנה ה-IFS (לרוב ספייס). (זה אומר שאם יש לנו רווחים בתוך הפרמטר - נקבל אותו בצורה של כמה מילים מופרדות)

  • $@ עובד דומה: יתן לנו רשימה של כל הפרמטרים, החל מ-1 כמילים נפרדות וכשהוא מוקף בגרשיים, יחזיר כל פרמטר מיקומי כמילה נפרדת (כאילו היו לה גרשיים - בניגוד ל-$*, כאן פרמטרים עם רווח ישארו "מאוחדים" (נניח, יעברו לפקודה אחרת כארגומנט אחד)

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

shift

  • באש עצמו משתמש רק ב-positional parameters אחת עד תשע. (לא בטוח שנכון, אנחנו עובדים עם shift פשוט כדי שנוכל לבנות סקריפט שירוץ על כל מס' של פרמרטרים) אז מה נעשה אם אנחנו צריכים שהסקריפט יעבד 100 אינפוטים? (נניח, יקבל רשימת קבצים מפקודה אחרת כארגומנט?) נשתמש בפקודה shift, שמזיזה את כל האינפוטים משתנה אחד "אחורה"

  • ברגע שהפעלנו את shift, משתנה $3 יהפוך ל-$2, משתנה $2 יהפוך ל-$1, ו-$1 ימחק, כי $0 לעולם לא משתנה. אז אם קיבלנו 10 ארגומנטים, נוכל לכתוב תוכנה שתרוץ רק על $1, ושתמשיך לקרוא אותו כל עוד $# גדול מ-0. לאחר כל קריאה, היא תסיים בפקודה shift. כך למעשה לא צריך יותר מאת $1 כדי לעבד את כל האינפוטים.

getopts

בפועל משתמשים ב-getopts בשילוב עם case כדי לתרגם פרמטרים לשורות קוד שירוצו... בעצם יהיה לנו while loop עבור getopts שזורם לתוך case שיגדיר מה קורה בעקבות כל דגל. - לא לבלבל עם getopt (בלי s) - בגדול פקודות שהפרמטרים שלהן הם flags עובדות לפי התבנית של getopts (תוכנת ה-flags בקיצור)

  • נותנת לנו עוד גישה לעיבוד של פרמטרים - כשעושים לה call, ה-getopt מעבירה את הערך של $1 למשתנה שנגדיר, ומעלה ב-1 את הערך של משתנה בשם $OPTIND (שמתחיל מ-1), כך שהערך שלו יצביע על הפרמטר המיקומי הבא (בעצם, אפשר להגיד שהיא תמיד מעבירה את OPTIND, כאשר OPTIND הוא הפרמטר המיקומי המתאים למספר שהוא מקבל, וכאשר הוא עולה ב-1 עם כל שימוש ב-getopt)

  • הסינטקס קצת מבלבל: מתחילים מ-getopts אחריה אנחנו מגדירים איזה פרמטרים יכולים להיות, מה שמכונה optstring אחרי אנחנו מגדירים את var, שהוא המשתנה שיקבל את הערך של הפרמטרים שהגדרנו, אם הם קיימים לפעמים דגל צריך ארגומנט: נניח שהדגל -o צריך filepath - אז ה-filepath שהיוזר יכניס יישמר במשתנה OPTARG אחרי var אפשר לתת ארגומנטים נוספים:

  • הגדרת ה-optstring:

  • ה-optstring בנויה מאותיות בודדות, שכל אחת מהן תפורש כהפעלה של אפשרות אם היא תופיע כדגל אחרי הפקודה -x
  • אם דגל צריך ארגומנט, נשים : אחרי האות
  • אם אנחנו רוצים להשתיק שגיאות שקשורות לפרמטרים לא קיימים/ארגומנטים לא טובים לפרמטרים, אז נתחיל את optstring עם : (רק לפני האות הראשונה וזה משתיק את כולן, אחרת לא היה הבדל בין s:m כ-"ל-s יש ארגומנט" ו-"את m משתיקים").
  • כמו שאנחנו יודעים, אינפוט ששם כמה פרמטרים תקניים תחת - אחד (נגיד ls -la ) הוא תקין עבור getopts getopts :f:ih opt : מגדיר 3 אותיות כפרמטרים, מתוכן f דורשת ארגומנט. כל השגיאות הקשורות בפרמטרים מושתקות.

  • אז איך היא רצה בפועל? כדי לחדד, בדוגמה לעיל, getopts תתחיל מלבדוק את $1 ולראות אם הוא כולל - (דאש) ואחריו את אחד הפרמטרים שהוגדרו לה. אם כן - היא תחזיר את הערך שלו (בלי דאש) ל-var ואת הארגומנט שלו, אם קיים, ל-OPTARG

    הפקודה תחליף את המונה של OPTIND ל-+1 מהערך הקודם, ובפעם הבאה שתרוץ, היא תעבד את $2. (או 3 אם כבר עשינו את 2...)

    הפקודה מחזירה exit status 0 כל עוד יש לה פרמטר תקין לעבד לכן אפשר לעשות: while getopts <getopts commands>; do והלולאה תמשיך עד שנסיים לעבד את הפרמטרים שהוזנו.

  • ארגומנטים אחרי var: בגדול getopts רצה על פרמטרים מיקומיים, אבל היא יכולה לעבד גם כל ארגומנט שניתן לה אחרי var (הגדרת המשתנה של הפלט) - אם נתנו לה ארגומנט, היא תחפש פרמטרים בארגומנט במקום באינפוט של הטרמינל

  • אאוטפוטים מיוחדים: ה-getopts תחזיר ? כאשר היא קיבלה - (דאש) עם אות שלא הוגדרה כפרמטר (או אולי כל אינפוט שהוא לא פרמטר)

    והיא תחזיר ; כאשר חסר ארגומנט לדגל שהגדרנו שמקבל ארגומנט

    בשני המקרים, האות שאחראית לשגיאה תודפס למשתנה OPTARG

  • USE CASE: יש לנו למעשה שתי אלטרנטיבות לעיבוד פרמטרים: או getopts או while+shift חשוב לזכור ש-getopts לא תומכת ב-long format (--help במקום -h נניח), בעוד שדי קל להגדיר פורמט כזה עם whife+shift ולעומת זאת, getopts דורשת פחות קוד ותומכת בשילוב דגלים (-la וכו')

trap

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

  • סינטקס: trap <argument> signal signal ... כאשר argument הן הפקודות שירוצו ו-signal ... הם רשימת הסיגנלים שזה חל לגביהם.

  • הנוהג הוא לציין פונקציה ב-command argument כדי לא לכתוב את כל הפרוטוקול בתוך הסינטקס של trap... פשוט כדי שיהיה מסודר יותר.

  • הפקודה תומכת בשני סיגנלים מיוחדים שהם לא בילט-אין ב-shell: מדובר ב-ERR שמתייחס לכל מצב של יציאה מהסקריפט עם סטטוס יציאה שאינו 0, ו-EXIT שמתייחסת לכל מצב שבו הסקריפט מסיים לרוץ. (לצורך העניין, הפקודות שנגדיר עבור ERR יודפסו בכל פעם שיש לנו סיום בשגיאה, והפקודות שנגדיר עבור EXIT יודפסו בכל פעם שהסקריפט יסיים לרוץ, מכל סיבה שהיא.

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

mktemp

  • תוכנת shell שנועדה לייצר temp files ושאר קבצים עם רכיבים אקראיים בשם. הסיבה שנרצה רכיבים אקראיים בתבנית השמות של ה-temp files שלנו היא כדי להימנע מהתנגשות עם קבצים אחרים במערכת, וכדי להימנע ממתקפות temp race (שבהן, אני מעריך, מריצים משהו על המחשב שלך באמצעות שינוי השם שלו לקובץ temp רווח שתכנה כלשהי תקרא).

  • שימוש: tempfile=($mktemp /temp/progream.$$.XXXX כל x שנשתמש בו מתרגם לספרה אקראית הפרמטר $$ מתרחב ל-PID של התוכנה שמריצה אותו הפקודה רק מחזירה את השם כאאוטפוט לכן הפכנו את האאוטפוט שלה לערך של משתנה tempfile (כך שנוכל לעשות touch $tempfile, נניח).

wait

  • פקודה שנועדה לאפשר asynchronous execution של סקריפטים ע"י סקריפטים אחרים הכוונה היא להרצה 'לא מסונכרנת' של סקריפטים מרובים, אבל השם מבלבל כי הפקודה מסייעת לנו דווקא לסנכרן את הפעילות שלהם.

    נניח שיש parent script ובתוכו יש פקודה שמריצה סקריפט נוסף - ה-child script כברירת מחדל הסקריפטים ירוצו סימולטנית מהנקודה שה-child התחיל לרוץ. אז מה אם אנחנו רוצים להשתמש בסקריפט חיצוני כמו בפונקציה? כלומר 'לזמן' אותו, לחכות שיסיים לרוץ, ורק אז להמשיך להריץ את parent script? בדיוק למטרה הזו יש את wait...

  • שימוש: wait <pid> הרפרור לסקריפט שנחכה לו הוא ע"י ה-PID שלו, כך שניתן לחכות לסיום של כל תהליך ולא רק של תהליך שהסקריפט עצמו הריץ. אם אכן מדובר ב-child script, לעתים קרובות נשתמש בפרמטר $! ב-parent, מיד אחרי שנריץ את ה-child. הפרמטר מתרחב ל-QPID של התהליך האחרון שהתחיל לרוץ ברקע ולכן להשתמש ב-wait !$ בעצם אומר לחכות שה-child יסיים. (מתחילים מ-wait, הפקודה מתהפכת בעברית...)

תהליכים וחלונות

ps aux

`ps aux | grep

הפקודה ps aux היא למעשה ps a+u+x

a = תראה תהליכים של כל היוזרים

u = תראה מידע מפורט

x = תראה גם תהליכים שלא נשלטים ע"י שום טרמינל פעיל

כן צריך לשים לב שהשימוש לא זהה לגמרי:

ps aux זה סינטקס של BSD

ps -a -u -x זה סינטקס של POSIX

הדגל -u ב-POSIX אומר "תהליכים של יוזר ספציפי" ומצופה מאיתנו לצרף אליו ארגומנט של היוזר המדובר (אז בקיצור aux זה שלוש אפשרויות אבל לא מקביל ל-a -u -x)

![[Pasted image 20250124202551.png|300]]

ps מראה תהליכים שרצים בשל הנוכחי בלבד

ps -e מראה את כל התהליכים

ps -ef נותן פירוט יותר מלא על כל התהליכים

ps aux ו-ps -ef - אותו דבר. בעבר אחד היה לפוסיקס ואחד לביאסדי. כיום שניהם עובדים.

[[לינוקס - כללי#תהליכים|על המידע שרואים ב-ps]]

top

יותר טוב מ-ps aux בלהראות יפה את התהליכים השונים אבל משום מה לא חבר טוב של grep (איטי ומפספס תהליכים)

(תיקון: שונה מ-ps בכך שהוא מראה תמונת מצב מתעדכנת של התהליכים, ולא תמונה קפואה)

top

top | grep <keyword>

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

![[Pasted image 20250124202825.png|300]]

nohup

להריץ פקודה ברקע (בלי טרמינל פתוח)

כמו שלמדנו על [[מילון BASH#kill - להרוג תהליכים|hangup signal]], בעצם כשתוכנה מקבלת את הסיגנל שהטרמינל שהפעיל אותה "ניתק" (נסגר), היא הרבה פעמים תיסגר גם.

nohup יעשה שהיא לא תיסגר כשהטרמינל שלה מנתק.

  • nohup (command)

  • כל ההודעות שקשורות לפקודה הזו יירשמו בקובץ nohup.out

  • הקובץ ישמר בתיקיה שממנה ניתנה הפקודה, או ב-home במקרה שאין הרשאות כתיבה לתיקיה הזו

  • אפשר לשלוח את האאוטפוט לקובץ אחר באמצעות פקודה כמו

nohup rclone sync /local/folder remote:/remote/folder > /path/to/custom_output.log 2>&1 &

(כדי להבין שווה לקרוא את [[#STDERR & STDOUT]])

  • command &

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

  • אם עושים גם NOHUP וגם & בסוף, גם נקבל את הטרמינל וגם נריץ את הפקודה ברגע

kill - להרוג תהליכים

  • kill (pid)

כדי לסגור תהליך
הרבה תהליכים יתעלמו מהסיגנל הרגיל, אפשר להגדיר kill -9 PID כדי לשלוח סיגנל 9, שהורג תמיד.

killall <process name> כדי לסגור את כל מה שהשם שלו מתאים לתבנית

-e כדי שיעבוד על התאמות מדויקות בלבד

-I כדי שיתעלם מהקייס

-i כדי שישאל לפני החיסול

-g כדי לחסל את כל התהליכים בקבוצה (של האחד שמתאים לחיפוש)

-s כדי לבחור איזה signal יישלח

סיגנלים של kill

חיסול של תהליך באמצעות kill או killall הוא למעשה שליחה של סיגנל מסוים לתהליך הזה. יש כמה סוגים.

kill -signal PID כדי לשלוח סיגנל מסוים

kill -l כדי לקבל פירוט של כולם

SIGHUP מחסל ומיד מתחיל מחדש

SIGKILL הורג וזהו

kill -s SIGHUP <process ID>

  • חשוב לדעת שגם Ctrl+c זה סיגנל (INT - interrupt) וגם Ctrl+Z (TSTP - Terminal Stop)

  • הדפאולט סיגנל, אם לא מציינים עבור kill, הוא term (terminate)

![[Pasted image 20250124205026.png]]

![[Pasted image 20250124205429.png]]

  • HUP הוא ההפך מ nohup

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

  • רק ה-Owner והסופריוזר יכולים לשלוח סיגנלים של kill לתהליך

killall

משמשת כדי לשלוח Kill signals לכמה תהליכים במקביל

killall [-u user] [-signal] processname

חייב להיות סופריוזר

killall -u tony * להרוג את כל התהליכים של טוני

killall -TSTP rclone לעשות סטופ לכל התהליכים שנקראים rclone

jobs, fg -- לפקודה שנפתחה מהטרמינל הנוכחי

  • jobs

כותב איזה תהליכים יש ברקע שרצו מהטרמינל הנוכחי ושאין להם nohup. לא עובד עם Nohup.

  • fg %(jobnumber)

מחזיר את מס' התהליך שנבחר ל-foreground. ואז אפשר לעשות לו ctrl+c ולעצור אותו.

Ctrl+Z - להקפיא פקודה

כמו שאפשר להרוג פקודה עם Ctrl+C, אפשר להקפיא פקודה ולהעביר אותה ל-background באמצעות Ctrl+Z

  • כמו כשנעשה &, אנחנו נקבל אאוטפוט עם מס' הג'וב שעבר לרקע

bg - להמשיך פקודה ברקע

  • נניח שהרצנו את xlogo (חלון X11 גנרי) ועשינו לו Ctrl+Z כדי להקפיא ולהזיז לרקע (שחררנו את הטרמינל).

  • איך מחזירים את הפקודה לפעולה? אפשר לעשות fg %jobnumber כמו שלמדנו, ואז נקבל אותו שוב פעיל ב-foreground (תופס לנו את הטרמינל)

  • או שאפשר לעשות bg %jobnumber כדי להחזיר את התוכנה לפעולה ברקע ולשחרר לעצמנו את הטרמינל

nice

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

לכל תהליך יש niceness, שזה כמה הוא "חביב" מבחינת ניצול משאבים

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

ה-niceness נמדדת ממינוס 20 (הכי פחות נחמד, הכי גבוה בעדיפות) עד 19 (הכי נחמד, הכי נמוך בעדיפות)

nice -n <value> process-name

כדי להריץ תהליך עם niceness מסוים, למשל:

nice -n 19 cpu-hog כדי להריץ את סיפיו-הוג במקסימום נחמדות

הברירת מחדל היא בד"כ niceness = 0

renice

בהמשך ל-nice, זו פקודה שמאפשרת לשנות את הפריוריטי של תהליכים לאחר שהם התחילו

renice -n <value> PID

power settings

אפשר להשתמש בטרמינל כדי לעשות halt, poweroff, reboot ו-shutdown

sudo reboot לריבוט

sudo shutdown -option <timer> בשביל כל השאר

האפשרויות: -h ל-halt

-r ל-reboot

-s ל-shutdown

-p ל-poweroff

הטיימר יכול להיות now או נניח 50s או 60m

עוד פקודות תהליכים

![[Pasted image 20250124210353.png]]

free

מדפיסה מידע לגבי שימוש בזיכרון.

שדות מעניינים:

  • השדה buffers מראה "באפרים" של כתיבה וקריאה מחומרה איטית, שבמקום להיעשות בזמן-אמת, נעשות על זיכרון וירטואלי/זמני שנערם עד שהחומרה עובדת/אוגר ממנה מידע בזמן שהיא עובדת.

journalctl

ה-Log הכי טוב לעקוב אחריו כדי לראות מה המערכת עושה בגדול.

הגרסה הידנית זה לעשות tail -f /var/log/syslog

במערכות מודרניות שיש להן systemd אפשר לעשות פשוט journalctl -f (שניהם אולי עם סודו)

חבילות

Zypper

[[Gnome 47 & OpenSUSE#Zypper|כאן]]

OPI

נותן לנו לחפש חבילות ב-AUR

חיפוש: ``opi aur package-name

להתקין ולשדרג package file

אם הורדנו .deb או .rpm, לא צריך את ה-GUI, אפשר להתקין באמצעות ה[[לינוקס - כללי#Packaging System|לואו-לבל טול]] שלנו.

לשים לב שבאופן הזה לא יהיה dependency resolution - אם הכלי שלנו יזהה dependency חסרה הוא יצא עם exit code error

dpkg -i package_file בדביאן

rpm -i package_file בפדורה וכו'

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

dpkg -i package_file (אותו דבר)

rpm -U package_file

(שוב מבקשים ממני לכתוב את השם ולא את ה-path... מניח שזה עובד אם אתה על התיקיה הנכונה)

מידע נוסף על חבילות (לואו-לבל)

dpkg -l או rpm -qa

כדי לקבל רשימה של כל החבילות (מהלואו-לבל טול)

dpkg -s packagename או rpm -q packagename

כדי לבדוק אם חבילה מותקנת...

כדי לבדוק על קובץ מסוים, איזו חבילה התקינה אותו:

dpkg -S file_name או rpm -qf file_name

רשת

בדיקות IP

  • Display External IP: curl ifconfig.me

w (who)

w נותן מידע אודות יוזרים במערכת

ping

ping hostaddress

שולח Packet בשם ICMP ECHO_REQUEST, שכברירת מחדל מכשירי רשת מגיבים אליו, וכך ניתן לאמת את החיבור.

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

ping linuxcommand.org לדוגמה כדי לבדוק חיבור

שולח packets ב-interval מוגדר עד שהוא עושה timeout או שמפריעים לו

בסוף ping כותב סיכום - רשת שפועלת כראוי היא כזו שרושמת 0 percent packet loss

traceroute

תוכנת טרמינל, לפעמים במקומה יש את tracepath הדומה.

נכתבת ככה traceroute hostaddress

מדפיסה לנו את ה-"hops" שהחיבור צריך לעבור מה-local system ל-host address, כלומר את כל התחנות שבדרך.

  • כל תחנה היא בעצם ראוטר שאנחנו עוברים - אנחנו מתחברים לראוטר ראשון וממנו "מנותבים" x פעמים מראוטר לראוטר.

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

  • לפעמים להריץ traceroute -T או -I יעזור לנו לראות מידע של ראוטרים שמציגים כוכביות

ip

תוכנת טרמינל, מאפשרת לנו להשתמש במגוון יכולות רשת של הקרנל, מחליפה את ifconfig הישנה.

ip address show כדי לראות את כל ה network interfaces שלנו

network interface הם החיבורים השונים שלנו לרשת.

loopback interface הוא חיבור רשת וירטואלי שהמחשב משתמש בו כדי "לתקשר עם עצמו"

en הוא חיבור אית'רנט

'wl הוא חיבור אלחוטי

  • חשוב לחפש ב-interfaces:

    1. את הביטוי state UP שאומר שהממשק פועל
  • כתובת ip תקנית בשדה inet

  • במערכות שמשתמשות ב-DHCP, השדה inet מאשר גם שה-DHCP פועל

ip route show כדי לראות את ה routing table

  • לקרוא על זה בהזדמנות

  • DEFAULT הוא הערך לכל כתובת שלא צוינה במפורש

ip [-options] object [command]

ftp

תוכנת טרמינל קלסית, נקראת על שם הפרוטוקול שהיא פועלת עליו: file transfer protocol

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

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

לכן, כיום יש בשימוש כמעט רק שרתי anonymous ftp, שניתן להתחבר אליהם באמצעות השם anonymous ואיזושהי ססמה. (הם מוצפנים וכו' ומיועדים שתיכנס אליהם בלי להכניס credentials שאתה באמת מזוהה איתם).

ftp fileserveraddress כדי להתחבר לשרת

נתבקש להכניס name - מקובל כאמור לשים anonymous

נתבקש לשים password יש שרתים שיקבלו blank, יש שרתים שירצו ססמה בפורמט של מייל: אפשר לעשות user@example.com

cd path/in/ftp

לרוב קבצים ישבו ב-cd pub/something

אפשר להשתמש בפקודת ls כדי לראות מה יש במקומות שונים בשרת

get filename כדי להוריד מהתיקיה שאנחנו נמצאים עליה בשרת

הקובץ ילך ל-working directory האחרונה במערכת המקומית

bye, quit, exit מסיים את החיבור וסוגר את ftp

lftp

תוכנה שהיא שדרוג של ftp, עובדת מאוד דומה אבל יש לה עוד הרבה אפשרויות

curl

באופן כללי, מורידה את העמוד הראשון של ה-URL שנספק לה ונותנת אותו כ-standard output

יכולה לקבל גם כמה urlים

תומכת בפרוטוקולים רבים כמו http, https, ftp, imap, pop3, sftp ועוד

curl URLaddress השימוש הבסיסי

![[Pasted image 20250126021710.png|500]]

איך curl מורידה לנו קבצים במקרים מסוימים?

wget

עוד תוכנת טרמינל להורדה של קבצים. תומכת ב-web וב-ftp.

אפשר להוריד קבצים מרובים.

השימוש הבסיסי זהה ל-curl:

wget URLaddress כדי להוריד את העמוד הראשון

  • הרבה אפשרויות מיוחדות שאין ב-curl

ssh

מערכות UNIX ניתנות לניהול מרחוק כבר הרבה זמן. בהתחלה, לפני האינטרנט, היה אפשר להשתמש ב-remote host באמצעות rlogin ו-telnet - שתיהן חלקו את הבעיה של ftp והעבירו מידע רגיש ללא הצפנה. לכן בעידן המודרני יש לנו את ssh - secure shel

הפרוטוקול החדש פותר שתי בעיות עיקריות:

  1. הוא מאמת שה-host הוא מי שהוא טוען שהוא (ומונע man in the middle attacks)

  2. הוא מצפין את כל התקשורת בין ה-local וה-remote host

  3. ברוב המערכות של לינוקס יש אימפלמנטציה של ssh בשם OpenSSH, שמגיעה מ-OpenBSD Project.

  4. ל-OpenSSH יש חבילות client (שנועדו להתחבר להוסטים) וחבילות server (שנועדו להיות הוסט ולקבל חיבורים) - כדי לקבל חיבורים צריך לוודא שיש לך את OpOpenSSH-server

  5. כדי לקבל חיבורים, אם יש לך פיירוול, צריך לאפשר חיבורים נכנסים בפורט TCP 22

  6. כשנוצר חיבור SSH, למעשה נוצר encrypted tunnel בין הלוקאל והרמוט. התעלה הזו היא תומכת בהעברה של פקודות בין שתי המכונות, ובסוגים נוספים של network traffic, ולמעשה מהווה VPN (Virtual Private Network) בין שתי המכונות.

  7. דוגמה מעניינת לשימוש בזה: X11 הוא display server. אפשר להריץ תוכנה במכונה אחת ולקבל את הפלט (חלון עם ממשק גרפי) במכונה ששלחנו ממנה את הפקודה להוסט.

ssh hostaddress כדי להתחבר לרמוט הוסט

בחיבור הראשון הוא יכתוב לנו שהוא לא יכול לאמת את האותנטיות. זה בגלל שהוא לא מכיר את ההוסט. נעשה y ונכניס ססמה. אחרי שמתחברים אנחנו על remote shell session עד שנכניס את הפקודה exit.

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

ssh username@hostaddress
ואז נכניס את הססמה.

  • לפעמים נקבל התראה כזו, על כך ש-ssh לא הצליח לאמת את ה-Host Key

![[Pasted image 20250127001318.png|500]]

  • סיבה אפשרית אחת היא שיש man in the middle attack (מישהו התלבש על ההוסט). זה לא מאוד פופולרי כיום כי כולם יודעים ש-ssh מזהיר מזה. אפשרות יותר סבירה היא שההוסט השתנה מאז הביקור האחרון (עבר עדכון מערכת או משהו נניח).

  • אם החלטנו שזה לא המצב, אפשר להתעלם מהאזהרה באמצעות

ssh-keygen -f "/home/me/.ssh/known_hosts" -R "remote-name"

  • זו למעשה דרך אוטומטית למחוק את ה-Host Key הלא-תקין מ-
  • ~/.ssh/known_hosts

  • באזהרה שנקבל כתוב ה-path תחת הכותרת offending key in, וכתוב שם גם באיזו שורה הוא נמצא, ככה: path/to/host_file:42 בשביל שורה 42, למשל.

  • לאחר שנמחק את ה-Host Key שכבר לא עובד, ssh יוכל לקבל אותנטיקציה חדשה מההוסט

פקודה בודדה באמצעות ssh

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

ssh remote-name command

אפשר גם להשתמש ב-redirectים וב-pipeים על האאוטפוט של הרמוט הוסט, וזה יפעל ברמת המערכת המקומית. למשל:

`ssh remote-name 'ls *' > dirlist.txt

יכין רשימת ls של כל הקבצים ברמוט הוסט, וידפיס אותה לקובץ dirlist שנמצא ב-working directory המקומית! ולא בהוסט.

  • כדי לתחום אילו פקודות הולכות ל-host ואילו יקרו על המכונה שלנו - משתמשים בגרשיים.

  • יש לזכור את העיקרון - 'command' בגרשיים עוברת כ-string ולא כפקודה (ללא expansion). אבל ה-string היא ללא הגרשיים - ואז הפקודה הופכת ל-command.

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

ssh remote-name 'ls * > dirlist.txt'

יעביר את הפלט של ה-ls לקובץ dirlist *ב-root של ה-host, כי הפקודה עברה במכונה כסטרינג, ואז אל ההוסט בלי הגרשיים,, כלומר כ-command!

WSL to Linux SSH

  • בגדול זה מדריך לאיך להתחבר לנייח שלי מהנייד שלי...
  • על ההוסט:
  • מוודאים שיש לנו את openssh-server
  • בודקים את האייפי שלנו באמצעות hostname -I (המספרים הוא לרשת מקומית, הארוך הוא לרשת ציבורית - שמים אותו בתוך [] כשמתחברים)
  • עושים sudo service ssh start (כי אין לנו systemd בשביל systemctl)
  • כשנרצה לסגור את ה-SSH (פרצה להאקרים...) נעשה sudo service ssh stop
  • על המחשב שמתחבר:
  • עושים את ה-ssh username@ip, מכניסים ססמה ושלום על ישראל
  • אם נכנסים מ-tmux session ל-tmux session, כדי לצאת פשוט להרוג את הסשן במחשב שהתחברנו ממנו...

scp & sftp

  • נכללים בחבילות של [[מילון BASH#ssh#|OpenSSH]] ומיועדים לאפשר העברה והעתקה של קבצים באמצעות הפרוטוקול

scp

  • הפקודה scp עובדת די דומה ל-cp, רק שצריך לציין גם את ההוסט של ה-path, ולהפריד בין שם ההוסט ל-path בנקודותיים :

  • scp remote-name:path/to/file remote-name:path/to/destination

  • (נתבקש להזין ססמה להוסט)

  • קיצורי דרך הולכים למכונה המקומית, למשל כדי להעתיק קובץ ל-working directory המקומי:

  • scp remote-name:pathtofile .

  • כמו עם ssh, אנחנו יכולים להשתמש בשם משתמש שאינו היוזרניים שלנו באמצעות @, ככה:

  • scp username@remote-name:path/to/file remote-name:path/to/destination

sftp

  • חבילה שמחליפה את ftp ועושה אותו דבר ב-encrypted tunnel במקום ב-cleartext

  • לא צריכה שלהוסט יהיה ftp server, רק שיהיה לו חיבור ssh ואפשר להשתמש בו כמו ב-ftp (להעביר ממנו קבצים)

  • sftp remote-name כדי להתחבר

  • זה יבקש מאיתנו ססמה

  • כמו שלמדנו עם [[מילון BASH#ftp#|ftp]], ננווט עם פקודות כמו cd, ls וכו', נוריד קובץ עם get filename ונסיים את הסשן עם bye

מערכות קבצים/devices

[[לינוקס - כללי#Filesystem Tree#|הקדמה]]

mount

הפקודה mount משמשת כדי לעשות mount ל-file systems

אם נשתמש בה בלי ארגומנט, נקבל רשימה של mounter devices עם קצת מידע עליהן.

כשאנחנו רוצים לעשות mount ל-mountpoint חדש, אנחנו פשוט צריכים לדאוג שהתיקיה הזו תהיה קיימת. למשל mkdir /mnt/flashdrive

mount -t vfat device-name path-to-mountpoint

הדגל -t משמש כדי לפרט את סוג מערכת הקבצים - במקרה הזה vfat שזה אותו דבר כמו לכתוב FAT32l.

ה-device-name זה השיט שנראה ככה בערך /dev/sda1 וכו'

ה-path-to-mountpoint זו התיקיה שיצרנו, שממנה מתחיל ה-file tree של ה-device

פקודה שלמה לדוגמה:

mount -t btrfs /dev/sdc /mnt/flash

לעשות mount ל-Image file

אם יש לנו קובץ iso שאנחנו רוצים לעשות לו mount כאילו הוא device:

משתמשים בהגדרה

-o loop

ובפורמט מערכת הקבצים iso9660

למשל ככה mount -t iso9660 -o loop image.iso /mnt/iso_image

הפורמט הוא למעשה:

mount -t iso9660 -o loop name-of-iso-file path/to/mount/point

כמו שרגיל לאחרונה, לא path אלא שם הקובץ. להיות על ה-directory הנכונה!

umount

כדי לעשות unmount ל-storage device נעשה umount devicename

(הכוונה לשמות שנראים ככה /dev/sdc וכו')

למה עושים unmount?

כי הכתיבה והקריאה מ-devices נעשית באמצעות buffer: במקום להיות עסוק כל היום בקריאה וכתיבה מחומרה איטית, המחשב שומר את האינטרקציות שלו עם ה-device בזיכרון זמני, שהוא הבאפר. כשאנחנו עושים umount אנחנו בין היתר אומרים למחשב לרוקן את ה-buffer הזה כדי שנוכל לנתק את המכשיר. אם לא נעשה את זה, אנחנו עלולים לגרום ל-File system corruption. [[מילון BASH#free|עוד מידע, פקודת free]]

lsblk

נותנת רשימה של כל ה-block devices (אחסון) שמחוברות למערכת, mounted או לא.

זו דרך לבדוק שם של device, בנוסף לדרך שמפורטת [[לינוקס - כללי#devices|כאן]]

ברגע שיש לנו את השם, הדרך לרפרר ל-device היא /dev/name - זה בגלל שהקבצים של ה-device יושבים ב-/dev בשם של ה-device

parted

תוכנת טרמינל שמאפשרת ליצור partition schemes ו-file systems (לפרמט דיסקים בקיצור)

sudo parted /dev/name כדי להתחיל

חייב לרפרר ל-device בשלמותה, בלי מס' ה-partition (אם יש לנו /dev/name1 ו-/dev/name2 למשל)

אחרי שהתחלנו:

print כדי לקבל את הפרטישן לייאאוט הנוכחי של הדבייס

rm partition-number כדי למחוק פרטישן לפי המספר

(לפני שעושים חלוקה חדשה חייב למחוק את כל הפרטישנז, אם יש רק אחת למשל אז rm 1

mkpart כדי ליצור פרטישן חדשה

נישאל את השאלות הבאות:

(parted) mkpart  

Partition type? primary/extended? primary  

File system type? [ext2]? ext4  

Start? 1  

End? 120000  

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

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

(parted) mkpart  

Partition type? primary/extended? primary  

File system type? [ext2]? ntfs  

Start? 120001  

End? 240000  

(הפעם יצרנו ntfs, כנראה לשימוש בווינדוז)

quit כדי לצאת מפארטד

mkfs

נועד כדי לייצר file system מסוים על disk partition - בעצם לפרמט את הפרטישן

sudo mkfs -t file-system-format -L file-system-name /dev/name

למשל:

sudo mkfs -t ext4 -L EXT4_Disk /dev/sdd1

כברירת מחדל, הפקודה תעשה גם bad block checking, שזה דבר שלוקח הרבה זמן. כדי לוותר, נעשה:

sudo mkfs -t file-system-format --quick -L file-system-name /dev/name

ה-LABEL שניתן למערכת הקבצים (אחרי -L יהיה השם של ה-mountpoint, כאשר הלייבל/הפרטישן בעצם תהיה תיקיה חדשה ב-mountpoint הכללי שהוגדר ל-device)

fsck

קיצור של File System Check

למערכת יש רוטינה של בדיקת file system integrity באמצעות התוכנה הזו

  • הערך האחרון ב-fstab קובע את הסדר של הבדיקה, מה שיש לו 0 לא נבדק

  • הפקודה גם מנסה לתקן בעיות באמצעות lost+found

sudo fsck /dev/name

ה-device צריכה להיות unmounted

  • אם במהלך ה-boot זוהה file system corruption, לרוב המערכת תנתב אותנו ל-fsck לפני כל דבר אחר

dd

אנחנו רגילים לחשוב על הדאטה שלנו בצורה של תיקיות וקבצים, אבל אפשר גם לחשוב על כל block device כאוסף גדול של data blocks

לכן, אפשר להשתמש ב-dd כדי להעתיק devices ולהעביר את המידע שבהן

dd if=input_file of=output_file [bs=block_size [count=blocks]]

מה שבסוגריים לא חובה.

לעתים קוראים ל-dd גם destroy disk, כי זו פקודה מאוד עצמתית שקל לטעות בה, אז לשים לב לכתיבה נכונה. (במציאות זה data definition )

דוגמה - להעתיק USB DRIVE אחד לשני (עותק מושלם):

sudo dd if=/dev/sdb of=/dev/sdc

אפשר (ומומלץ) לפרק את הפקודה לשני חלקים: העתקה של המידע לקובץ חיצוני, והעתקה של הקובץ ל-Drive השני. משתמשים בקובץ img או בקובץ iso

ככה:

sudo dd if=/dev/sdb of=flash_drive.img

ואז ככה

sudo dd if=flash_drive.img of=/dev/sdc

(הוא מראש מניח שאנחנו משתמשים ב-file, ולהשתמש מיד ב-device name זה הקיצור דרך, לא להיפך)

לכתוב CD-ROM באמצעות dd

  • נניח שיש לנו דיסק של אובונטו שאנחנו רוצים לייצא ממנו קובץ ISO

  • dd if=/dev/cdrom of=ubuntu.iso

  • עובד בשביל data dvd, לא עובד בשביל audio CD! בשביל לחלץ מידע מדיסקים של מוזיקה אפשר לבדוק את הפקודה cdrdao

genisoimage

תוכנת טרמינל שיודעת להפוך תיקיית קבצים שלמה ל-ISO IMAGE

נשים את כל הקבצים של ה-CD בתיקיה מסוימת ונריץ

genisoimage -o cd-name.iso -R -J path/to/cd/directory

-R מפעיל את Rock Ridge extensions, משהו שמספק מטאדאטה

-J מפעיל Joilet extensions - מאפשר שמות ארוכים בווינדוז???

בעבר, כל מה שקשור ל-CDROM ו-DVD התבצע באמצעות חבילה בשם cdr-tools שכללה את mkisofs ואת cdrecord.

היוצר שינה משהו ברשיונות והם כבר לא מתאימים ל-GNU

אז קהילת הלינוקס יצרה שני פורקים שיחליפו את הכלים האלה: wodim ו-genisoimage

wodim

  • משמש כדי למחוק דאטה מדיסק שניתן לשכתוב, נראה גם "blanking it"

wodim dev=/dev/name blank=[fast]

כותבים את fast בלי הסוגריים. יש עוד אפשרויות לסוג ה-blanking (המחיקה) שיתבצע. פאסט זה המהיר והרגיל.

  • בנוסף יכול לכתוב דאטה על דיסק שניתן לשכתוב:

  • wodim dev=/dev/name image-file-name.iso

  • יש מלא אפשרויות נוספות כמו disc-at-once או track-at-once, לקרוא את man wodim אם מעניין

sha256sum

פרוטוקול לבדיקת integrity של קבצי image. נניח שהורדנו iso של לינוקס כלשהו ואנחנו רוצים לראות שהקובץ אינו פגום - נשתמש בזה.

זו בעצם תוכנה מחודשת לתוכנה בשם md5sum שהייתה בשימוש בעבר.

  • ה-checksum הוא בעצם ביטוי של הדאטה של הקובץ בערכים הקסדצימליים.

  • שינוי קטן במבנה הבינארי של הדאטה ייצור שינוי משמעותי בערך ההקסדצימלי של ה-checksum

  • כשאנחנו מורידים קובץ IMAGE כלשהו, נחפש אם סיפקו לנו קובץ checksums

  • אם כן, הוא יראה למשל ככה:

7a04b54830004e945c1eda6ed6ec8c57ff4b249de4b331bd021a849694f29b8f *linuxmint-22-cinnamon-64bit.iso  

78a2438346cfe69a1779b0ac3fc05499f8dc7202959d597dd724a07475bc6930 *linuxmint-22-mate-64bit.iso  

55e917b99206187564029476f421b98f5a8a0b6e54c49ff6a4cb39dcfeb4bd80 *linuxmint-22-xfce-64bit.iso  

אפשר לראות פה את ערך ה-checksum התקין עבור כל קובץ

  • עכשיו צריך להוציא את ה-checksum העדכני של הקובץ הרלוונטי ולבדוק אם הוא מתאים בדיוק.

sha256sum -b name-of-image-file.iso

הדגל -b מסמל ל-sha256sum שאנחנו בודקים קובץ בינארי ולא txt

  • אפשר גם לבקש ממנו להשוות בשבילנו את ה-checksum עם מה שנמצא בקובץ:

  • sha256sum -c --ignore-missing name-of-checksum-file.txt

הדגל -c אומר לו לבצע את ה-check שמדובר עליו (כנגד הקובץ)

הדגל --ignore-missing אומר לו לבדוק רק קבצים שקיימים בתיקיה שלנו (הקובץ checksum מכיל כמה sums והוא כותב לנו את השם של כל אחד אחרי ה-*. אנחנו רוצים שיבדוק קובץ שקיים אצלנו, ושיתעלם מקובץ שלא קיים)

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

חידודים: קודם כל, כן, הוא יודע כי ה-checksum file והקובץ שנבדק יושבים באותה תיקיה.

שנית כל: הפרמוט של קובץ ה-checksum מאוד פשוט:

checksumstring *filename

ואם יש עוד קבצים, בשורות חדשות.

du

du -s מדפיס רשימה של כל הפאת'ים בדיסק וכמה מקום הם תופסים

אפשר לעשות לזה pipe ל-sort ואז למשל ל-head בשביל הטופ x ערכים (נניח לפי משקל)

du -s /usr/share/* | sort -nr | head לא לשכוח שסורט עם דגל אן מסדר לפי גודל מספרי, אז משקל במקרה שלנו

הדפסה

pr

  • מפרמט קובץ או STDIN להדפסה, מאפשר להוסיף header margin ו-footer margin

זה הפורמט

pr -l pagelength(lines) -w pagewidth(columns) sourcefile.txt

![[Pasted image 20250201161931.png|500]]

ps2pdf

  • ממיר קבצי PostScript לקבצי PDF ככה:

ps2pdf originalfile.ps desiredfilename.pdf

  • הפקודה היא חלק מחבילת ghostscript שמותקנת על רוב מערכות הלינוקס

  • בלינוקס יש הרבה תוכנות המרה שאפשר למצוא על ידי חיפוש של כל התוכנות שנקראות x2x או xtox ב-/usr/bin/*

CUPS

  • ללינוקס כיום יש את CUPS - Common Unix Printing System שמספקת תוכנות ניהול הדפסה ודרייברים, ואת Ghostscript שמתפקדת כמפרש PostScript ואחראית על ה-RIP

  • בקיצור סויטת תוכנות הדפסה

  • מארגנת את ההדפסה לפי Print Queue שכולל jobs ו-users, מבוסס על הדפסה בימי קדם, כשהייתה למשרד מדפסת מרכזית שכולם שלחו אליה הדפסות

  • עבודות (jobs) נחשבות ב-'חניה' (parked) עד שהן מועברות להדפסה (spooled)

  • הכל ממודל לפי שתי מערכות הדפסה מימי קדם, כשהייתה לנו רק הדפסה מרכזית: מערכת אחת היא ברקלי Berkeley, ואחרת היא System V

lpr & lp

  • השיטה שלנו לשלוח הדפסות ל-CUPS

  • יש גם lpr וגם lp שהן מאוד דומות

  • output | lpr שולח להדפסה למדפסת שהיא ברירת המחדל שלנו

  • lpr -P printer-name שולח למדפסת אחרת שנבחר

  • lpstat -a יכתוב לנו איזה מדפסות המערכת מכירה

  • הרבה פעמים אפשר להשתמש בפיצ'ר מוכר של לשלוח ל-pdf במקום להדפסה אמתית. צריך לבדוק ב-lpstat אם המערכת מאפשרת; אם לא, אפשר להתקין cups-pdf כדי שתהיה לנו אפשרות (המטרה היא גם ליצור pdf, אבל בעיקר לקבל preview להדפסה)

![[Pasted image 20250201162256.png|500]]

  • ל-lp יש אפשרויות קצת יותר מתקדמות:

![[Pasted image 20250201162348.png|500]]

a2ps

  • תוכנה שממירה כל דבר ל-PostScript, במקור נועדה להמיר ASCII

  • היא לא שולחת ל-STDOUT אלא שולחת למדפסת של המערכת... אז היא לא באמת תוכנת המרה כמו שהיא תוכנת הדפסה

![[Pasted image 20250201162626.png|500]]

  • יש עוד הרבה אפשרויות ב-man page

  • יש גם את enscript שעובדת דומה אבל רק עם text input

lpstat

  • נותנת לנו מידע על הפעילות (זמינות וכו') של מדפסות מוכרות למערכת

  • lpstat -a זה הרגיל

  • lpstat -s בשביל קצת יותר מידע

![[Pasted image 20250201163012.png|500]]

  • עוד אפשרויות ב-man

lpq

  • lpq מראה לנו את הקיו הנוכחי של המדפסת שהיא ברירת המחדל

  • מעניין אם אפשר לראות של עוד מדפסות

lprm & cancel

  • שתי תוכנות שנועדו לבטל print jobs, ולהסיר אותן מהקיו

  • lprm היא לברקלי סטייל

  • cancel היא ל-System V

  • cancel number-of-printjob

  • lpq יעבוד על הדפאולט

  • שתי התוכנות יכולות לבטל עבודות לפי כל מיני סינונים - לקרוא ב-man page

קומפילציה

make

  • פקודה שמריצה הוראות של קובץ makefile, שיצרנו בתוך תיקיה של סורס קוד, אחרי שהרצנו את קובץ ה-configue באמצעות ./configure ויצרנו את ה-makefile

  • כתבתי הסבר מפורט על קומפילציה ועל make [[לינוקס - כללי#תהליך הקומפילציה|בקובץ הכללי, 'תהליך הקומפילציה']]

טרמינולוגיה (לא פקודות)

Exit Gracefully

"Exit gracefully" refers to the process of closing or terminating a program, process, or system in a controlled, orderly manner, ensuring that it does not leave any unfinished tasks, errors, or data inconsistencies behind. The goal is to stop operations in such a way that the system remains stable, and no damage occurs to files or resources.

argument

המבנה הכללי של פקודה:

command -options arguments

ה-argument הוא ה-item שהפקודה פועלת עליו (בהתאם לפקודה ולדגלים/אפשרויות שנבחרו עבורה)

End of Life

אם עושים control + d זה בעצם מסמל לטרמינל שהסטנדרט אינפוט שלו הסתיים.

אם נניח נעשה cat בלי ארגומנט, הוא יחכה שנקליד לו אינפוט.

אפשר לכתוב דברים, ואז לעשות ctrl + d כדי לאותת לו שסיימנו (הוא ידפיס את הטקסט)

expansion

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

ככה למשל עם כוכבית, שמסמלת כידוע "כל תו"

אם נעשה echo * זה לא ידפיס כוכבית, אלא ידפיס את כל הקבצים המתאימים ב-working dir

לאופן שבו גלובים 'מתרחבים' קוראים pathname expansion

קבצים חבויים:

לעשות echo .* יתן לנו את כל הקבצים החבויים, אבל גם את התיקיה הנוכחית ואת התיקיית אם שלה, שהן . ו-...

כדי לקבל תוצאה בלעדיהן, צריך לעשות echo .[!.]*, שזו תוצאה שמתרחבת לכל קובץ שמתחיל בנקודה ולאחריו בא לפחות תו אחד.

ls -A נותן לנו את התוצאה הנכונה בקלות

Tilde expansion

זה איך ש-~ הופך ל-home

אפשר גם ~user בשביל ה-home של יוזר ספציפי

Arithmetic expansion


חידוד: כשלעצמו, ביטוי בתוך ((...)) לא צריך $ בהתחלה. לכתוב את הסוגריים כשלעצמן זה כמו לכתוב פקודה, שיכולה לעשות שני דברים: 1. לבצע פעולה אריתמטית (אם נכתוב ((a++)), אז נוסיף 1 ל-a 2. להחזיר סטטוס יציאה 1/0 (אמת/שקר לאופרטורים בינאריים, ומס' חיובי/שלילי לתוצאות שאינן בינאריות) רק כשאנחנו רוצים 'לרפרר' לתוצאה אנחנו צריכים להשתמש ב-$ למשל לפני echo או כאשר מגדירים משתנה לפי התוצאה.

שימוש בסיסי: $((expression))
מתרחב לתוצאה של הביטוי המתמטי.

![[Pasted image 20250124021643.png]]

למשל echo $(($((5**2)) * 3)) יחזיר פשוט 75
לא תומך בשברים - אם נעשה חילוק שהתוצאה שלו לא שלמה נקבל integer (חילוק עם שארית) - אפשר לקבל את השארית עם % כמו שכתוב בטבלה


בסיסים: - אפשר לפרמט את המספרים שאנחנו משתמשים בהם באמצעות בסיסים שונים (מספר 'רגיל' הוא אינטגר Integer, 'בסיס 10' - יש לנו אוקטאליים שהם בסיס 7, ועוד) (הפרמוט הוא פשוט באיך שנכתוב את המספר)


פעולות: - יש Unary Operators שהם +, - ו-~, בכנות, לא הבנתי מה הם עושים - יש את כל פעולות המתמטיקה הרגילות: לגבי % (שארית) - אם עושים a % b אנחנו נקבל את שארית החלוקה של a ו-b. שימושי למשל כדי לבדוק אם השארית 0 ולהפסיק ש-b כפולה של a.

השוואה: - כדי לבדוק אם a שווה בערכו ל-b, נשתמש ב-'שווה-שווה' == הסיבה היא ש-'שווה' בודד הוא תמיד assignment: אם נעשה למשל if (( var = 5 )), מה שיקרה זה שבתור התניה, אנחנו קובעים ש-var=5, וכל משתנה חיובי מחזיר 0/true (כשלעצמו), לכן ההתניה מקבלת true.

**חשוב לזכור שרק ב-test הישן **(`[...]`) אפשר להשתמש ב-`=` בודד להשוואה בין משתנים** (כי הוא מאוד ישן וחורג מהנורמה, שהיא == עבור השוואה)

לוגיקה

  • אופרטורים לוגיים משמשים גם למתמטיקה, מדובר בכל אופרטור שמחזיר true or false (האאוטפוט יהיה באקסיט קוד 1 או 0 בהתאם)

|500 ~ מעניין ה-ternary opetator (האחרון ברשימה) - הוא לקוח משפת C והוא בעצם נוסחה לוגית קצרה שנכתבת כאופטרור עצמאי: אם הביטוי הראשון הוא true, אז תבצע את הביטוי השני; אם הביטוי הראשון false אז תבצע את הביטוי השלישי. חשוב לשים לב שמדובר בלבצע את הביטוי השני או השלישי, כלומר, ניתן לבדוק ערך אמת עבור אופרטורים לוגיים, או לבצע שינוי בערך של משתנה מספרי. ((a<1?++a:--a)) בעצם יעשה toggle אם a קטן מ-0, הוא יוסיף 1 ל-a; אם a גדול מ-0, הוא יחזיר אותו ל-0 (פעולה אחת ל-'הדלקה' ו-'כיבוי'.) כדי לתת בביטוי 2 או 3 פקודה של assignment: אי אפשר לכתוב פשוט a=b (לא ברור למה) יש לכתוב בתוך סוגריים נוספות, ככה: expr1?(a=b):(a=c)


אפשרויות Assingment - כמו שעושים "משתנה = ערך", ישנם עוד אופרטורים להגדרת משתנה:

~ חשוב לשים לב להבדל בין parameter++ ו-++parameter הסינטקס הזה לקוח משפת C, וגם שם זה עובד ככה: parameter++ מחזיר את המשתנה ואז מעלה אותו באחת ++parameter מעלה אותו באחת ואז מחזיר את המשתנה echo $((parameter++)) יתן לנו את הערך של המשתנה ורק אז יעלה אותו באחת echo $((++parameter)) יעלה את המשתנה באחת ורק אז ידפיס אותו (עובד אותו דבר עם המינוסים)


פעולות על ביטים: |500

brace expansion

echo one-{A,B,C}-two

יתרחב ל-

one-A-two one-B-two one-C-two

לרישה קוראים premable ולסיפה קוראים postscript

אפשר לשים string עם quotes, ספרה או אות

בלי unquoted whitespace

אפשר גם לעשות zero padding:

{Z..A}

בשביל כל האותיות בסדר הופכי (מ-Z ל-A)

{01..15}

בשביל כל הספרות מ-01 עד 15

{001..15}

בשביל כל הספרות מ-001 עד 015

שימושי ליצירת קבצים ותיקיות

נגיד בא לי מלא תיקיות לפי שנה וחודש:

mkdir {2001..2010}-{01..12}

ייצור תיקיה לכל שנה וחודש

אז חשוב לזכור שאם עושים command {range}-{range} אנחנו נקבל את כל ההצלבות האפשריות של הריינג'!

parameter expansion

  • האופן שבו $var מתרחב לערך של המשתנה (הרגיל או הסביבתי)
  • להקליד לא נכון את השם סתם יחזיר ערך ריק

  • נשתמש בגרשיים כדי לדאוג שהפרמטר יתרחב בצורתו המקורית לפעמים אנחנו רוצים להשתמש בפרמטר בתוך string גדול יותר, ורוצים שיתרחב כאילו היו לו גרשיים. כל פרמטר אפשר להציג לבאש כשהוא מוקף בסוגריים מסולסלות - נניח $1 ו-${1} הם שקולים מבחינתו. לכן, במקום לעשות: echo ""$1" was selected" (שזו צורה לא נכונה - הגרש הראשון נסגר מיד עם השני...) נעשה: echo "{1} was selected ואז באש יבין את 1 כמוקף בגרשיים בפני עצמו, וגם את הסטרינג כמוקף בגרשיים


אפשרויות להרחבת פרמטרים:

  • אפשר להגדיר ברירת מחדל לפרמטר באמצעות := נניח שאנחנו רוצים להדפיס את פרמטר $1, ואם הוא ריק, להדפיס "null" אז עושים ככה:
    ${parameter:=<default>}
    
  • (אם 1 ריק, אז הביטוי יתרחב ל-default, ובנוסף, הערך של 1 יהפוך ל-default) לא עובד על positional parameters

  • אפשר להגדיר error output לפרמטר ריק באמצעות :? ${parameter:?output} יתרחב כרגיל אם יש ערך ל-parameter, ואם לא, הסקריפט יצא עם קוד 1, וידפיס את output ל-STDER

  • אפשר להגדיר אאוטפוט שיחליף את הפרמטר דווקא כשיש לו ערך, באמצעות :+ ${parameter:+substitution} ידפיס את substitution בכל מקרה בו יש ערך ל-parameter, ולא ידפיס כלום/ידפיס את הפרמטר (הינו הך) כשהפרמטר ריק

  • יש שתי פקודות זהות שנועדו להדפיס את כל המשתנים שעונים לדפוס מסוים (כל המשתנים המתאימים שהוגדרו בסביבה של ה-shell): ${!prefix*} ${!prefix@} (* ו-@ מתפקדים כאן כגלובים? מעניין אם אפשר לחפש *suffix(כוכבית בהתחלה)


עוד אפשרויות לפרמטרים שהם סטרינגס:

  • ${#parameter} ידפיס מס' לפי כמות האותיות בערך הפרמטר אם הפרמטר הוא *, זה ידפיס את מס' ה-positional parameters

  • ${parameter:offset:length} ידפיס סטרינג רק החל מאות מסוימת בה אפשר לעשות parameter:5 כדי פשוט להתחיל מהאות החמישית או parameter:5:10 כדי 'לחתוך' מהאות החמישית עד האות העשירית אפשר לעשות parameter:-5 כדי להתחיל מהאות החמישית מהסוף

  • ${parameter#pattern} יסיר מההרחבה את התבנית שנגדיר מתוך הסטרינג אפשר ורצוי להשתמש בגלובים כדי להגדיר את ה-pattern # יסיר את ההתאמה הקצרה ביותר אפשר לעשות parameter##pattern כדי להסיר את ההתאמה הארוכה ביותר ואפשר לעשות % או %% במקום הסולמית, כדי להסיר את ההתאמות מהסוף ולא מההתחלה


Case conversion:

  • מיועד כדי להדפיס פרמטר בצורה מנורמלת לתבנית מסוימת של lowercase & uppercase

|500

command substitution

[[מילון BASH#$() פלט כמשתנה#|מדובר על פלט כמשתנה]]

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

echo $(ls)

יהפוך ל echo <output of ls>

סטנדרט ישן יותר לפעולה הזו שנתמך ב-Bash הוא פשוט להשתמש בגרשיים בודדות

echo 'ls'

history expansion

!88 יורחב להיות הפקודה ה-88 בהיסטוריה של באש

אפשר לבדוק את ההיסטוריה [[מילון BASH#history#|ככה]]

![[Pasted image 20250124180303.png|500]]

quotes

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

echo $100

echo I want this space

לא יעבדו כי הטרמינל ירחיב או יתעלם בהתאם.

double quotes

  • נועד כדי לקשור טקסט עם רווחים, פקודות וכו' ל-ARGUMENT יחיד

  • במובן הזה קצת הופכי ל-single quote - אנחנו כותבים אותו כדי לאחד טקסט ככה שהוא יעבוד, עם פקודות והתרחבויות והכל, במקום לשמור אותו string

מתעלם מכל התווים המיוחדים חוץ מ$, ו-'

כלומר: הרחבת פרמטרים, אריתמטיקה ו-command substiution עדיין עובד

אבל כל השאר לא

echo "you can have spaces.txt"

single quotes

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

echo 'even $ or \ won't work here'

Compound

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

  • לצורך העניין, [[ ... ]] (Double Brackets), או integer brackets (( ... ))

Shell sessions

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

  • אנחנו פותחים Terminal Emulator, שהיא תוכנה גרפית שיודעת לקבל אינפוט מ-command line ולהציג אאוטפוט על text buffer.

לתוכנה הזו יש קצת פקודות CLI משלה, שנועדו לתפעול הבסיסי שלה (לסייר בקבצים, לטעון תוכנה, לשנות הגדרות וכו')

  1. אנחנו טוענים באמצעותו Shell Program: במקרה שלנו מדובר ב-Bash, אבל יש גם zsh, fish, narrow/posix shell ועוד

  2. כשתוכנת ה-shell רצה (לפחות במקרה של Bash), היא למעשה יוצרת Shell Session:

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

  1. מכאן ואילך, כל תהליך שה-shell תריץ יהיה Sub Shell חדש.

כלומר Shell session עצמאי, שיורש את כל התכונות שלו מה-Parent Shell, שזה ה-Session שהריץ אותו.

  • זו הסיבה שלהריץ תהליך חדש מתוך ה-Shell הנוכחי שלנו לא ירענן את bashrc עבור אותו תהליך. התהליך יורש את הסביבה של ה-Parent Shell שלו.

  • פייפים |, סוגריים עגולות () ועוד כל מיני פקודות מריצים את האינפוט שלהם ב-Sub Shell Session, ולכן הקוד ששייך לאינפוט שלהם לא ישפיע על משתנים ב-Session שאנחנו עובדים ממנו

  • באופן כללי: ה-Subshell לעולם לא יכול להשפיע על התכונות של ה-Parent Shell - זה יחס היררכי.

  • הפוך דווקא אפשרי: הפקודה export מייצאת ערך מסוים לכל ה-Subshells של הסשן שהריץ אותה.

זו הסיבה שאנחנו שמים export למשתנים ב-bashrc, אנחנו רוצים שיגררו גם ל-child processes שלנו.

אבל אפשר גם להגדיר משתנה ולעשות export var (או לעשות export var=value ולחסוך שורה), וכך לשנות את הערך של המשתנה var בכל ה-Subshells בזמן אמת

  • באופן כללי, כשאנחנו מריצים סקריפט, הוא רץ ב-Subshell

אם הסקריפט שלנו צריך להגדיר משתנים ב-Parent Shell, הוא צריך לרוץ על ה-Parent.

כדי להריץ סקריפט על ה-Shell session הנוכחי נשתמש בפקודה source

הפקודה source scriptname תהיה חייבת להיכתב ב-session הנוכחי, כלומר, היא לא יכולה להיות חלק מהסקריפט (להריץ סקריפט מיד פותח סשן חדש).

פיתרון אפשרי זה להכניס בסקריפט פקודה ליצור alias של source scriptname בפעם הראשונה שהוא רץ... (כמו שעשיתי עם historyfinder)