טיפ באש: השתמשו ב null glob כשצריך

16/08/2021

את הלולאה הקצרה הזאת כתבתי בסקריפט בלי לחשוב יותר מדי:

#!/bin/bash -e

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

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

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

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

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

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

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

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

$ shopt -s nullglob
$ echo /data/*

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

#!/bin/bash -e

shopt -s nullglob

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

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