סקריפטים ב־Bash הם אחת הדרכים הכי מהירות להפוך פעולות חוזרות בלינוקס וב־macOS לאוטומציה פשוטה: העתקת קבצים, בדיקת לוגים, ניקוי תיקיות זמניות, הרצת פקודות לפי תנאי, או חיבור כמה כלים קטנים לפעולה אחת. לא צריך לבנות מערכת שלמה, לא צריך מסגרת עבודה כבדה, ולא צריך להתחיל מ־Python אם כל מה שרוצים הוא לגרום לשורת הפקודה לעבוד בשבילכם.
במאמר הזה נכתוב Bash בצורה מעשית: משתנים, לולאות ותנאים, עם דוגמאות מלאות שאפשר להריץ כמו שהן. הדגש הוא על אוטומציה בטוחה, קריאה וניתנת לתחזוקה. אם אתם ממש בתחילת הדרך עם טרמינל, כדאי לפתוח במקביל גם את מדריך צעדים ראשונים, ואז לחזור לכאן ולתרגל את הדוגמאות.
מה צריך להכין מראש
הדוגמאות במאמר מיועדות ל־Linux/macOS עם Bash. ברוב מערכות לינוקס Bash כבר מותקן. ב־macOS הוא קיים, אך לעיתים בגרסה ישנה יותר; הדוגמאות כאן נשארות בסיסיות בכוונה כדי לעבוד גם שם.
- טרמינל פתוח.
- עורך טקסט פשוט, למשל
nano,vimאו עורך גרפי. - יכולת להריץ פקודות בסיסיות כמו
pwd,ls,cd. - תיקייה זמנית לתרגול. מומלץ לא להריץ סקריפטים ראשונים בתוך תיקיות עבודה חשובות.
כל הדוגמאות יוצרות קבצים זמניים או עובדות בתוך תיקייה זמנית שנמחקת בסוף. אין כאן מחיקה של קבצי מערכת, אין שימוש בסיסמאות, ואין צורך בהרשאות מנהל.
למה Bash טוב לאוטומציה יומיומית
Bash מצטיין במשימות שמחברות בין כלים קיימים. במקום לפתוח קובץ, לחפש שורות, להעתיק תוצאה ולהריץ פקודה נוספת, אפשר לכתוב סקריפט קצר. זה מתאים במיוחד למשימות כמו:
- סריקה של קבצים לפי סיומת.
- בדיקת מצב דיסק או תיקייה.
- חיפוש תבניות עם
grepו־regex. - יצירת גיבוי קטן לפני שינוי קבצים.
- הרצת פקודות Git כמו
git stashאוgit rebaseכחלק מרצף עבודה מקומי, בזהירות ובהבנה.
כאשר הלוגיקה נהיית מורכבת מאוד, או כשצריך מבני נתונים עשירים, בדיקות, עבודה עם API או עיבוד טקסט מתקדם, יכול להיות שכדאי לעבור ל־פייתון. אבל להרבה משימות קו־פקודה קטנות, Bash הוא בדיוק הכלי הנכון.
משתנים ב־Bash: הכל מתחיל בשם וערך
משתנה ב־Bash מוגדר בלי רווחים סביב סימן השוויון. זו אחת הטעויות הראשונות והנפוצות ביותר. כותבים name="backup", לא name = "backup".
| פעולה | דוגמה | הערה |
|---|---|---|
| הגדרת משתנה | folder="logs" |
אין רווחים סביב = |
| שימוש במשתנה | "$folder" |
כמעט תמיד לעטוף במרכאות |
| ערך ברירת מחדל | ${1:-80} |
אם לא התקבל ארגומנט, השתמש ב־80 |
| חישוב מספרי | $((count + 1)) |
מתאים לחישובים שלמים פשוטים |
דוגמה 1: העתקת קבצי טקסט לתיקיית גיבוי
הדוגמה הבאה מדגימה משתנים, לולאת for, בדיקת קיום קובץ, ושימוש ב־basename. היא יוצרת סביבת תרגול זמנית, מעתיקה רק קבצי .txt, ומדפיסה מה הועתק.
#!/usr/bin/env bash
set -u
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
mkdir -p "$tmpdir/inbox" "$tmpdir/backup"
printf 'monthly report\n' > "$tmpdir/inbox/report.txt"
printf 'meeting notes\n' > "$tmpdir/inbox/notes.md"
prefix="demo"
copied=0
for file in "$tmpdir"/inbox/*.txt; do
[ -e "$file" ] || continue
base=$(basename "$file")
cp "$file" "$tmpdir/backup/${prefix}_${base}"
copied=$((copied + 1))
done
echo "Copied $copied file(s)"
for file in "$tmpdir"/backup/*; do
[ -e "$file" ] || continue
basename "$file"
doneכדי להריץ, שמרו את הקוד בקובץ למשל backup-demo.sh, ואז הריצו bash backup-demo.sh. הפלט הצפוי הוא:
Copied 1 file(s)
demo_report.txtשימו לב לשורה [ -e "$file" ] || continue. היא מגינה עלינו ממקרה שבו אין קבצי .txt בתיקייה. בלי הבדיקה הזאת, התבנית עצמה עלולה להישאר כמחרוזת ולהוביל לתוצאות מבלבלות.
תנאים: לקבל החלטות בתוך הסקריפט
תנאי ב־Bash נכתב לרוב עם if, בדיקה בתוך [ ] או [[ ]], ואז then. התנאי מאפשר לסקריפט לא רק לבצע פקודות, אלא לבחור מה לעשות לפי מצב המערכת, קלט מהמשתמש או תוצאה של פקודה קודמת.
בדיקות נפוצות:
[ -f "$file" ]— האם זה קובץ רגיל.[ -d "$dir" ]— האם זו תיקייה.[ "$count" -gt 0 ]— האם מספר גדול מאפס.[[ "$name" == *.log ]]— האם מחרוזת מתאימה לתבנית.
דוגמה 2: בדיקת שימוש בדיסק עם ערך ברירת מחדל
הסקריפט הבא מקבל סף באחוזים כארגומנט. אם לא מעבירים סף, הוא משתמש ב־80. הוא בודק את מחיצת התיקייה הנוכחית באמצעות df, שולף את האחוז עם awk, ומחליט אם להדפיס אזהרה.
#!/usr/bin/env bash
set -u
limit="${1:-80}"
usage=$(df -P . | awk 'NR == 2 { gsub("%", "", $5); print $5 }')
if [ "$usage" -ge "$limit" ]; then
echo "Disk usage is high: ${usage}% (limit: ${limit}%)"
else
echo "Disk usage is OK: ${usage}% (limit: ${limit}%)"
fiשמרו כ־disk-check.sh והריצו למשל bash disk-check.sh 90. הפלט תלוי במחשב שלכם, אבל ייראה כך:
Disk usage is OK: 42% (limit: 90%)או במקרה אחר:
Disk usage is high: 91% (limit: 90%)זו דוגמה קטנה אבל שימושית: אפשר להריץ אותה ידנית, להכניס אותה למשימת תחזוקה מקומית, או להרחיב אותה כך שתבדוק כמה תיקיות. חשוב לא להפוך אותה מיד למנגנון שמוחק קבצים לבד. באוטומציה טובה, במיוחד בתחילת הדרך, עדיף קודם לדווח ורק אחר כך לבצע פעולה שמשנה מצב.
לולאות: לבצע אותה פעולה על הרבה פריטים
לולאות הן הלב של הרבה סקריפטים. במקום להריץ אותה פקודה על עשרה קבצים ידנית, נותנים ל־Bash לעבור עליהם. יש כמה סוגים, אבל בפועל תשתמשו הרבה ב־for וב־while read.
forמתאים לרשימה פשוטה של קבצים, מילים או ערכים.while IFS= read -r lineמתאים לקריאה בטוחה של שורות מקובץ או פלט של פקודה.- כמעט תמיד כדאי לעטוף משתנים במרכאות:
"$file".
כלל אצבע חשוב: אם שם קובץ יכול להכיל רווחים, מרכאות הן לא קישוט — הן הגנה מפני באגים.
דוגמה 3: סריקת לוגים עם regex וספירת אזהרות
כאן נשתמש ב־grep -E כדי לחפש תבנית regex פשוטה: שורות שמכילות ERROR או WARN. הסקריפט יוצר שני קובצי לוג זמניים, סורק אותם, ומדפיס רק קבצים שיש בהם התאמות.
#!/usr/bin/env bash
set -u
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
mkdir -p "$tmpdir/logs"
cat > "$tmpdir/logs/app.log" <<'LOG'
INFO application started
WARN cache is almost full
ERROR failed to load optional file
LOG
cat > "$tmpdir/logs/worker.log" <<'LOG'
INFO worker started
INFO job completed
LOG
pattern='(ERROR|WARN)'
while IFS= read -r file; do
count=$(grep -E -c "$pattern" "$file" || true)
if [ "$count" -gt 0 ]; then
echo "$(basename "$file"): $count"
fi
done < <(find "$tmpdir/logs" -type f -name "*.log" | sort)הרצה עם bash log-scan.sh תחזיר:
app.log: 2הקובץ worker.log לא מודפס כי אין בו שורות שמתאימות לתבנית. השימוש ב־|| true אחרי grep חשוב במיוחד כאשר עובדים עם set -e, כי grep מחזיר קוד יציאה שאינו אפס כאשר אין התאמות. זה לא בהכרח כישלון; לפעמים זו תוצאה תקינה לגמרי.
איך לבנות סקריפט Bash שלב אחר שלב
אל תתחילו מסקריפט גדול. דרך עבודה בריאה נראית כך:
- מריצים את הפקודה ידנית בטרמינל על דוגמה קטנה.
- שומרים אותה בקובץ
.sh. - מחליפים ערכים קבועים במשתנים.
- מוסיפים תנאי שמוודא שהתיקייה או הקובץ קיימים.
- מוסיפים לולאה רק אחרי שהפקודה עובדת על פריט אחד.
- מדפיסים מה הסקריפט עומד לעשות לפני שמבצעים פעולה שמשנה קבצים.
לדוגמה, לפני פקודת rm עדיף להריץ קודם echo עם אותו נתיב ולבדוק שהפלט נכון. גם בסקריפטים פשוטים, זה ההבדל בין אוטומציה מועילה לבין תקלה מעצבנת.
הרשאות והרצה: bash script.sh מול ./script.sh
יש שתי דרכים נפוצות להריץ סקריפט:
bash script.sh— מריץ את הקובץ באמצעות Bash גם בלי הרשאת הרצה../script.sh— דורש הרשאת הרצה ושורתshebangתקינה, למשל#!/usr/bin/env bash.
כדי לאפשר הרצה ישירה:
chmod +x script.sh
./script.shבתרגול ראשוני, bash script.sh מספיק לגמרי. בפרויקטים מסודרים יותר, הרשאת הרצה ושורת shebang עוזרות להפוך את הקובץ לכלי קטן שאפשר להריץ בקלות.
טעויות נפוצות והודעות שגיאה אמיתיות
Permission denied
אם הרצתם ./script.sh וקיבלתם:
bash: ./script.sh: Permission deniedהקובץ כנראה לא קיבל הרשאת הרצה. פתרון:
chmod +x script.sh
./script.shאו פשוט:
bash script.shbad interpreter: No such file or directory
הודעה נפוצה אחרי עריכה ב־Windows:
bash: ./script.sh: /usr/bin/env: bad interpreter: No such file or directoryאו לעיתים תראו סימן ^M בתוך ההודעה. המשמעות היא שהקובץ נשמר עם סיומות שורה של Windows. אפשר לתקן כך:
sed -i 's/\r$//' script.shב־macOS הפקודה מעט שונה:
sed -i '' 's/\r$//' script.shunary operator expected
שגיאה כזו:
./check.sh: line 7: [: -gt: unary operator expectedבדרך כלל אומרת שמשתנה שהיה אמור להכיל מספר נשאר ריק. במקום:
if [ $count -gt 0 ]; then
echo "found"
fiכתבו בצורה בטוחה יותר:
count="${count:-0}"
if [ "$count" -gt 0 ]; then
echo "found"
fisyntax error near unexpected token `then'
אם כתבתם את התנאי בשורה אחת בלי נקודה־פסיק, למשל:
if [ "$count" -gt 0 ] then
echo "found"
fiתקבלו שגיאת תחביר. תקנו לאחת משתי הצורות:
if [ "$count" -gt 0 ]; then
echo "found"
fiאו:
if [ "$count" -gt 0 ]
then
echo "found"
fiהרגלים קטנים שעושים סקריפטים טובים יותר
- השתמשו בשמות ברורים:
backup_dirעדיף עלbd. - עטפו משתנים במרכאות:
"$file"במקום$file. - התחילו בפעולות קריאה בלבד: בדיקה, הדפסה, ספירה. רק אחר כך שינוי.
- בדקו כל שלב בנפרד: אם
findמחזיר רשימה לא נכונה, הלולאה רק תגדיל את הבעיה. - הכירו כלי בדיקה:
shellcheckיכול לזהות הרבה טעויות Bash נפוצות לפני הרצה.
אם אתם מרגישים שהסקריפט מתחיל להתמלא בהרבה תנאים, עיבוד טקסט מורכב וקבצי קלט שונים, זה סימן טוב לעצור ולשאול אם Bash עדיין מתאים. לפעמים התשובה תהיה כן. לפעמים מעבר ל־פייתון יהפוך את הקוד לברור יותר, במיוחד אם צריך בדיקות, פונקציות רבות או טיפול מסודר בשגיאות.
סיכום: לא לבנות מחדש, אלא לשדרג את העבודה היומיומית
סקריפטים ב־Bash לא אמורים להחליף כל כלי אחר. הם דרך פרקטית לשדרג פעולות קיימות: לקחת רצף פקודות שאתם כבר מריצים, לתת לו שם, להוסיף משתנים, תנאים ולולאות, ולהפוך אותו לכלי קטן שחוסך טעויות וזמן.
התחילו ממשימה אחת: ספירת שורות לוג, העתקת קבצים, בדיקת דיסק או יצירת תיקיית עבודה. כתבו גרסה קצרה, הריצו על דוגמה זמנית, בדקו את הפלט, ורק אז הרחיבו. כך בונים אוטומציה שנשארת מובנת גם חודש אחרי שכתבתם אותה.


