السكريبت الذي يُضمّن إعداداته في الكود هو سكريبت لا يمكن إعادة استخدامه. كل أداة شل احترافية — من kubectl إلى aws إلى أتمتة النشر الخاصة بك — تقبل وسائط تغير سلوكها وقت الاستدعاء. في هذا الدرس ستتعلم مجموعة أدوات تصميم الواجهات الكاملة لسكريبتات Bash: المعاملات الموضعية، والأمر المدمج getopts للأعلام على نمط POSIX، ونمط دالة الاستخدام الكنسي، والأمر المدمج read لتلقي إدخال المشغل البشري. هذه المكونات الأربعة كافية لبناء واجهات سطر أوامر لا تختلف عن أدوات Unix القياسية.
المعاملات الموضعية
عند استدعاء Bash لسكريبت، يصل كل كلمة بعد اسم السكريبت إلى متغير مُرقَّم. $1 هو الوسيطة الأولى، و$2 الثانية، وهكذا. $0 هو اسم السكريبت نفسه. $# هو عدد الوسائط. "$@" يتوسع ليشمل جميع الوسائط ككلمات مقتبسة منفصلة — افضله دائمًا على $* عند تمرير الوسائط، لأنه يحافظ على المسافات البيضاء داخل القيم الفردية.
#!/usr/bin/env bash
set -euo pipefail
# $1 = البيئة (مطلوب) $2 = وسم الصورة (اختياري)
ENVIRONMENT="${1:-}"
IMAGE_TAG="${2:-latest}"
# فشل سريع برسالة واضحة بدلًا من خطأ غامض لاحقًا
if [[ -z "$ENVIRONMENT" ]]; then
echo "ERROR: وسيطة البيئة مطلوبة" >&2
exit 1
fi
echo "نشر الصورة ${IMAGE_TAG} إلى ${ENVIRONMENT}"
# shift يزيل $1 ويُعيد ترقيم الباقي؛ مفيد بعد استهلاك الوسائط المعروفة
shift
echo "الوسائط المتبقية بعد shift: $*"
# تكرار آمن على جميع الوسائط
for arg in "$@"; do
echo " وسيطة: ${arg}"
done
صيغة ${1:-} تمنحك سلسلة فارغة عند غياب الوسيطة، حتى يعمل فحص -z بشكل نظيف. الصيغة الأكثر صرامة ${1:?رسالة} تجعل Bash يطبع الرسالة ويخرج فورًا — مفيدة داخل الدوال لكنها قد تُنتج مخرجات مربكة في مستوى السكريبت الأعلى.
فكرة رئيسية — تحقق دائمًا قبل الاستخدام: لا تفترض أبدًا أن المستدعي مرر العدد الصحيح من الوسائط. تحقق مبكرًا بجملة حارسة أو دالة استخدام، واخرج برمز غير صفري (عادةً 1 أو 2) ورسالة إلى stderr. هذا ما تفعله كل أداة Unix مكتوبة بجودة، وهو ما يتوقعه المشغلون عند توصيل سكريبتك في سير عمل أكبر.
نمط دالة الاستخدام
كل سكريبت يقبل وسائط يحتاج دالة usage. توثّق الواجهة، وتُطبَع عند -h/--help، وتُستدعى تلقائيًا عند فشل التحقق. هذا هو النمط المستخدم في HashiCorp وGitHub Actions runner ومعظم أدوات البنية التحتية مفتوحة المصدر:
#!/usr/bin/env bash
set -euo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
usage() {
cat <<EOF
الاستخدام: ${SCRIPT_NAME} [الخيارات] <البيئة>
نشر التطبيق في البيئة المحددة.
الوسائط:
environment البيئة المستهدفة: dev | staging | prod
الخيارات:
-t, --tag TAG وسم صورة Docker للنشر (الافتراضي: latest)
-n, --dry-run طباعة الإجراءات دون تنفيذها
-v, --verbose تفعيل المخرجات التفصيلية
-h, --help عرض رسالة المساعدة والخروج
أمثلة:
${SCRIPT_NAME} staging
${SCRIPT_NAME} -t v2.3.1 prod
${SCRIPT_NAME} --dry-run prod
EOF
}
# حارس: طباعة الاستخدام والخروج بـ 0 عند -h/--help كأول وسيطة
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
لاحظ cat <<EOF — heredoc عادي (ليس NOWDOC) حتى يتوسع ${SCRIPT_NAME} بشكل صحيح داخل نص الاستخدام. دالة الاستخدام هي أول ما يقرأه مشغل جديد؛ اجعلها كاملة ودقيقة.
تحليل الأعلام باستخدام getopts
getopts هو الأمر المدمج القياسي POSIX في Bash لتحليل الخيارات القصيرة (-v، -t TAG). يتعامل مع تجميع الخيارات (-vn)، والوسائط المطلوبة، والحارس -- لنهاية الخيارات. لا يتعامل مع الخيارات الطويلة (--verbose) بشكل أصلي؛ لهذه استخدم حلقة while/case يدوية أو getopt الخارجي. في الممارسة، تستخدم معظم سكريبتات الإنتاج في الشركات الكبيرة النمط اليدوي لأنه يتعامل مع الصيغتين القصيرة والطويلة بشكل نظيف.
#!/usr/bin/env bash
set -euo pipefail
# --- الافتراضيات ---
IMAGE_TAG="latest"
DRY_RUN=false
VERBOSE=false
# --- getopts: الخيارات القصيرة فقط ---
# النقطتان بعد الحرف تعنيان أن الخيار يتطلب وسيطة.
# النقطتان في البداية تُفعّل معالجة الأخطاء الصامتة.
while getopts ":t:nvh" opt; do
case "$opt" in
t) IMAGE_TAG="$OPTARG" ;;
n) DRY_RUN=true ;;
v) VERBOSE=true ;;
h) usage; exit 0 ;;
:) echo "ERROR: الخيار -${OPTARG} يتطلب وسيطة" >&2; exit 2 ;;
\?) echo "ERROR: خيار مجهول -${OPTARG}" >&2; exit 2 ;;
esac
done
# بعد getopts، انقل الخيارات المُعالَجة حتى يصبح $1 أول وسيطة موضعية
shift $(( OPTIND - 1 ))
ENVIRONMENT="${1:?$(usage; echo 'ERROR: البيئة مطلوبة')}"
echo "الوسم=${IMAGE_TAG} جاف=${DRY_RUN} مفصل=${VERBOSE} البيئة=${ENVIRONMENT}"
كيف يعالج getopts الأعلام واحدًا تلو الآخر، يُوزّع إلى فرع case، ثم يترك الوسائط الموضعية للاستخدام بعد shift.
التعامل مع الخيارات الطويلة بحلقة while/case
للسكريبتات التي يستخدمها البشر بكثرة، تُحسّن الخيارات الطويلة (--dry-run، --tag) القراءة بشكل كبير. النمط القياسي هو حلقة while true; do case "$1" in ... esac; done يدوية:
#!/usr/bin/env bash
set -euo pipefail
IMAGE_TAG="latest"
DRY_RUN=false
VERBOSE=false
while [[ $# -gt 0 ]]; do
case "$1" in
-t|--tag)
IMAGE_TAG="${2:?--tag يتطلب قيمة}"
shift 2
;;
-n|--dry-run)
DRY_RUN=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-h|--help)
usage
exit 0
;;
--)
shift # حارس صريح لنهاية الخيارات
break
;;
-*)
echo "ERROR: خيار مجهول: $1" >&2
usage
exit 2
;;
*)
break # أول وسيطة غير-خيار؛ توقف عن تحليل الأعلام
;;
esac
done
ENVIRONMENT="${1:?ERROR: وسيطة البيئة مطلوبة}"
shift || true
echo "نشر ${IMAGE_TAG} إلى ${ENVIRONMENT} | جاف=${DRY_RUN} مفصل=${VERBOSE}"
ممارسة احترافية — رمز الخروج 2 لأخطاء الاستخدام: اتفاقية POSIX هي رمز الخروج 1 لأخطاء وقت التشغيل ورمز الخروج 2 لـ"الاستخدام الخاطئ" (وسائط خاطئة، علم مجهول). كثير من أنظمة CI وسكريبتات الشل تختبر $? ويمكنها التمييز بينهما. اخرج دائمًا بـ2 عندما يمرر المستدعي وسائط سيئة، واطبع الاستخدام أو مؤشرًا إلى --help.
الإدخال التفاعلي باستخدام read
لا ينبغي أبدًا أن تتطلب خطوط الأنابيب الآلية إدخالًا تفاعليًا — لكن السكريبتات التي يُشغّلها مشغل بشري (التوفير، وترحيل قواعد البيانات، وتدوير الأسرار) تحتاج أحيانًا إلى تأكيد أو قيمة لا يمكن للسكريبت اشتقاقها. يتعامل الأمر المدمج read مع هذا بشكل نظيف.
#!/usr/bin/env bash
set -euo pipefail
# موجه أساسي — يخزن الإجابة في REPLY افتراضيًا
read -r -p "أدخل البيئة المستهدفة [dev/staging/prod]: " ENVIRONMENT
# قراءة بقيمة افتراضية — اضغط Enter للقبول
read -r -p "وسم الصورة [latest]: " IMAGE_TAG
IMAGE_TAG="${IMAGE_TAG:-latest}"
# قراءة كلمة مرور دون ظهور الأحرف على الطرفية
read -r -s -p "كلمة مرور قاعدة البيانات: " DB_PASSWORD
echo "" # سطر جديد بعد الإدخال المخفي
# قراءة بمهلة — تُعيد قيمة غير صفرية إذا انتهت المهلة
if ! read -r -t 30 -p "متابعة النشر؟ [y/N]: " CONFIRM; then
echo ""
echo "انتهت المهلة. إلغاء." >&2
exit 1
fi
# حارس تأكيد بمطابقة regex
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
echo "تم إلغاء النشر بواسطة المشغل."
exit 0
fi
echo "المتابعة: البيئة=${ENVIRONMENT} الوسم=${IMAGE_TAG}"
أعلام read الرئيسية التي يجب معرفتها: -r تُعطّل تفسير الشرطة المائلة للخلف (استخدمها دائمًا)، -p تطبع موجهًا، -s تُخفي الصدى (كلمات المرور)، -t N تضبط مهلة بالثواني، و-a تقرأ كلمات مفصولة بمسافات إلى مصفوفة.
فخ الإنتاج — الإدخال التفاعلي في CI: عند تشغيل سكريبت يحتوي على read داخل خط أنابيب CI (GitHub Actions، Jenkins، GitLab CI)، لا يكون stdin طرفيةً ويُعيد read فورًا سلسلة فارغة أو تنتهي مهلته. تحقق دائمًا من [[ -t 0 ]] (هل stdin طرفية؟) قبل الموجه، أو وفّر علمًا --yes / -y يتخطى جميع الموجهات للاستخدام غير التفاعلي. دمج المسارين التفاعلي وغير التفاعلي في نفس السكريبت هو النمط القياسي المستخدم في أدوات مثل helm upgrade وkubectl apply.
تجميع كل شيء: هيكل سكريبت جاهز للإنتاج
الجمع بين كل ما تعلمناه في هذا الدرس ينتج الهيكل المستخدم في سكريبتات البنية التحتية الحقيقية في شركات التقنية الكبرى. النمط هو: دالة الاستخدام، والافتراضيات، وحلقة تحليل الأعلام، والتحقق من الموضعيات، وموجه تأكيد مُدرك لـCI، ثم العمل الفعلي:
#!/usr/bin/env bash
# rollback.sh — التراجع عن خدمة إلى وسم صورة سابق
# الاستخدام: ./rollback.sh [الخيارات] <الخدمة> <الوسم>
set -euo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
usage() {
cat <<EOF
الاستخدام: ${SCRIPT_NAME} [الخيارات] <الخدمة> <الوسم>
التراجع عن <الخدمة> إلى صورة Docker <الوسم> في الكلستر الحالي.
الخيارات:
-n, --namespace NS مساحة اسم Kubernetes (الافتراضي: default)
-y, --yes تخطي موجه التأكيد (للـCI/الأتمتة)
-v, --verbose تفعيل مخرجات kubectl التفصيلية
-h, --help عرض المساعدة
EOF
}
# --- الافتراضيات ---------------------------------------------
NAMESPACE="default"
YES=false
VERBOSE=false
# --- تحليل الأعلام ------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--namespace) NAMESPACE="${2:?--namespace يتطلب قيمة}"; shift 2 ;;
-y|--yes) YES=true; shift ;;
-v|--verbose) VERBOSE=true; shift ;;
-h|--help) usage; exit 0 ;;
--) shift; break ;;
-*) echo "ERROR: خيار مجهول $1" >&2; usage; exit 2 ;;
*) break ;;
esac
done
# --- التحقق من الموضعيات ------------------------------------
SERVICE="${1:?$(usage; echo 'ERROR: وسيطة الخدمة مطلوبة')}"
TAG="${2:?$(usage; echo 'ERROR: وسيطة الوسم مطلوبة')}"
# --- بوابة التأكيد (تُتخطى في CI) --------------------------
if [[ "$YES" == false ]]; then
read -r -p "التراجع عن ${SERVICE} إلى ${TAG} في المساحة ${NAMESPACE}؟ [y/N]: " CONFIRM
[[ "$CONFIRM" =~ ^[Yy]$ ]] || { echo "تم الإلغاء."; exit 0; }
fi
# --- العمل الفعلي -------------------------------------------
KUBECTL_FLAGS=()
[[ "$VERBOSE" == true ]] && KUBECTL_FLAGS+=(-v=6)
echo "التراجع عن ${SERVICE} إلى وسم الصورة ${TAG} ..."
kubectl set image deployment/"${SERVICE}" \
"${SERVICE}=${SERVICE}:${TAG}" \
--namespace "${NAMESPACE}" \
"${KUBECTL_FLAGS[@]}"
echo "تم. راقب الطرح باستخدام:"
echo " kubectl rollout status deployment/${SERVICE} -n ${NAMESPACE}"
هذا الهيكل — دالة الاستخدام، والافتراضيات، وحلقة الأعلام، وحارس الموضعيات، والتأكيد المُدرك لـCI، ثم العمل — هو القالب الذي يجب أن يُكرره فريقك في كل سكريبت بنية تحتية جديد. يعمل بشكل صحيح سواء استدعاه إنسان أو خط أنابيب CI أو سكريبت آخر.
نستخدم ملفات تعريف الارتباط لتشغيل هذا الموقع وتحليل الزيارات وعرض إعلانات مخصّصة. يمكنك قبول كل ملفات تعريف الارتباط أو رفض غير الأساسية منها.
سياسة الخصوصية