الوضع الصارم باستخدام set -euo pipefail
فعّل سلوك التوقّف عند الفشل وافهم بدقة الأخطاء التي يلتقطها كل خيار من خيارات الوضع الصارم والتي لا يلتقطها
الوضع الصارم باستخدام set -euo pipefail درس مجاني في Linux Command Line & Bash Scripting Mastery على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Linux Command Line & Bash Scripting Mastery، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
لماذا يفشل Bash بصمت افتراضيًا
يواصل Bash التنفيذ افتراضيًا حتى عند فشل الأوامر. ويؤدي ذلك إلى كوارث دقيقة يصعب تصحيحها في البرامج النصية المستخدمة في الإنتاج.
انظر إلى هذا البرنامج النصي الذي يحاول إنشاء نسخة احتياطية:
- يتسبب خطأ إملائي في مسار ما في فشل
cp - يتجاهل Bash الفشل ويواصل التنفيذ
- يُبلغ البرنامج عن نجاح العملية رغم أن البيانات لم تُنسخ احتياطيًا قط
هذه هي مشكلة الفشل الصامت. يحلها الوضع الصارم بجعل Bash يتصرف مثل لغة مُصرَّفة: يتوقف فورًا عند حدوث خطأ.
#!/usr/bin/env bash
# Without strict mode — dangerous default behavior
cp /important/data /backups/data # fails (path doesn't exist)
echo "Backup complete" # still prints — false confidence!
rm -rf /tmp/staging # still runs — potentially destructiveالرايات الأساسية الثلاث: set -euo pipefail
يُفعَّل الوضع الصارم بوضع هذا السطر بالقرب من بداية كل برنامج نصي:
set -euo pipefail
يُفعّل هذا السطر ثلاث آليات حماية مختلفة:
-e— الخروج فورًا إذا أعاد أي أمر حالة غير صفرية-u— اعتبار المتغيرات غير المُعيّنة خطأً (بدل توسيعها إلى سلسلة فارغة)-o pipefail— يفشل خط الأنابيب إذا فشل أي أمر فيه، وليس الأمر الأخير فقط
تشكل هذه الخيارات معًا الترويسة الدفاعية القياسية للبرامج النصية الجادة في Bash. وتلتقط كل راية نوعًا مختلفًا من الأخطاء.
#!/usr/bin/env bash
set -euo pipefail
echo "Strict mode is now active"
echo "Every command failure will abort the script"فهم set -e (errexit)
يتسبب set -e (ويُكتب أيضًا set -o errexit) في إنهاء البرنامج النصي فورًا عندما ينتهي أمر بحالة غير صفرية.
السلوكيات الأساسية التي ينبغي معرفتها:
- الأوامر البسيطة:
falseوgrep pattern file(عند عدم العثور على تطابق) وls /nonexistentتؤدي جميعها إلى الخروج - تصبح شيفرة خروج آخر أمر في البرنامج النصي شيفرة خروجه
- الأوامر الموجودة في شروط
ifمستثناة — فلا يُفعّل-eلتعبير الاختبار - الأوامر المتبوعة بـ
|| trueمستثناة أيضًا (انظر المشاهد التالية)
فكّر في -e باعتباره خط دفاعك الأول ضد مواصلة التنفيذ بصمت بعد حدوث فشل.
#!/usr/bin/env bash
set -e
echo "Before failure"
ls /this/path/does/not/exist # exits here with code 2
echo "This line never runs"فهم set -u (nounset)
يجعل set -u (ويُكتب أيضًا set -o nounset) Bash يعتبر أي مرجع إلى متغير غير مُعيّن خطأً فادحًا.
من دون -u، يتمدد خطأ إملائي مثل $FLENAME بدلًا من $FILENAME بصمت إلى سلسلة فارغة، مما يؤدي إلى تصرف الأوامر على نحو غير متوقع — أو خطير (تخيّل تنفيذ rm -rf "$TMPDIR/" عندما يكون $TMPDIR غير مُعيّن).
استثناءات مهمة:
${VAR:-default}— استبدال افتراضي آمن، ولا يفعّل-u${VAR:+value}— توسيع شرطي آمن أيضًا- يُستثنى
"$@"و"$*"عند عدم تمرير وسيطات موضعية
#!/usr/bin/env bash
set -euo pipefail
# Safe: provide a default for optional vars
OUTPUT_DIR="${1:-/tmp/output}"
LOG_LEVEL="${LOG_LEVEL:-info}"
echo "Writing to: $OUTPUT_DIR"
echo "Log level: $LOG_LEVEL"
# This would abort the script:
# echo "$UNDEFINED_VAR" # bash: UNDEFINED_VAR: unbound variableفهم -o pipefail
من دون pipefail، تتحدد حالة خروج خط الأنابيب بالكامل وفقًا للأمر الأخير. أما حالات الفشل السابقة فتُبتلع بصمت.
مثال من دون pipefail:
cat /missing/file | wc -l- يفشل
catبشيفرة خروج تساوي 1، لكنwc -lينجح بشيفرة تساوي 0 - يعيد خط الأنابيب القيمة 0 — نجاح! رغم فقدان البيانات.
عند تفعيل pipefail، يعيد Bash شيفرة خروج آخر أمر فاشل من جهة اليمين. ويجعل ذلك حالات فشل خطوط الأنابيب ظاهرة وقابلة للاكتشاف.
ملاحظة: إن pipefail ليس راية حرفية — بل يجب ضبطه باستخدام -o pipefail.
#!/usr/bin/env bash
set -euo pipefail
# With pipefail: this aborts if grep finds nothing (exit 1)
# grep returns 1 when no match found
ps aux | grep "[n]ginx" | awk '{print $2}'
echo "If we reach here, nginx is running"إخفاقات الأوامر المقصودة — استخدام || true
قد يُسمح أحيانًا لأحد الأوامر بالفشل. مع set -e، يجب التصريح صراحةً بحالات الفشل المقبولة؛ وإلا أُجهض البرنامج النصي.
الحل الاصطلاحي هو || true، إذ يضيف مسارًا احتياطيًا ينجح دائمًا:
command || true— تجاهل الفشل بالكاملcommand || echo "Warning: step failed, continuing"— سجّل تحذيرًا وتابع التنفيذcommand || { echo "fatal"; exit 1; }— معالجة مخصصة للفشل
يجعل هذا النمط نيتك واضحة في الشيفرة: الأمر العادي يعني «يجب أن ينجح هذا»؛ أما || true فتعني «قد يفشل هذا، ولا بأس في ذلك».
#!/usr/bin/env bash
set -euo pipefail
# Remove temp dir if it exists — OK if it doesn't
rm -rf /tmp/my_workspace || true
mkdir -p /tmp/my_workspace
# Check if a service is running — OK if not
if systemctl is-active --quiet nginx 2>/dev/null || true; then
echo "nginx is active"
fi
# Grep that may find nothing — OK
grep 'ERROR' /var/log/app.log || true
echo "Done"ما لا يلتقطه set -e
لدى set -e استثناءات ومزالق معروفة. ويساعد فهمها على تجنب الثقة الزائفة:
- الأوامر الموجودة في شروط
ifأوwhileأوuntil— يُستثنى تعبير الاختبار عن قصد - الأوامر المنفية باستخدام
!— لا يؤدي! falseإلى الخروج - آخر أمر قبل
||— مثلfalse || handle_error - حالة خروج الصَدَفة الفرعية في سياقات معينة — مثل
VAR=$(failing_command)في بعض إصدارات Bash - القيم المرجعة من الدوال — لا يُحتسب إلا الأمر الأخير في الدالة
الوضع الصارم ليس بديلًا عن التحقق الصريح من الأخطاء — بل هو شبكة أمان تلتقط غالبية حالات الفشل غير المقصودة.
#!/usr/bin/env bash
set -euo pipefail
# These do NOT trigger -e:
if false; then echo "never"; fi # -e exempt in conditions
! false # negation exempts
false || echo "handled" # || exempts the left side
# This DOES trigger -e (no condition, no ||):
# false
echo "Script continues after exempted failures"الصدفات الفرعية والدوال مع الوضع الصارم
تُورث إعدادات الوضع الصارم من الصدفات الفرعية، لكنها تتصرف بطريقة دقيقة داخل الدوال واستبدالات الأوامر.
القواعد الأساسية:
- ترث الدوال الخيارات
-eو-uوpipefailمن الصَدَفة المستدعية - يتسبب إرجاع الدالة لقيمة غير صفرية في خروج المستدعي (عند ضبط
-e) — إلا إذا استُدعيت الدالة ضمن شرط أو بعد|| - استبدال الأوامر
$(): في إصدارات Bash الأقدم، قد لا يؤدي فشل أمر داخل$()إلى تفعيل-eفي الصَدَفة الأصلية؛ لذا أسند القيمة ثم استخدمها في خطوة منفصلة بأمان - ترث الصدفات الفرعية الصريحة
()جميع الرايات
#!/usr/bin/env bash
set -euo pipefail
setup_workspace() {
local dir="$1"
mkdir -p "$dir" # fails here if permissions denied
cd "$dir"
echo "Ready in $(pwd)"
}
# Safe pattern: assign result then use it
TODAY=$(date +%Y-%m-%d) # capture separately
WORKDIR="/tmp/run_${TODAY}"
setup_workspace "$WORKDIR"
echo "Workspace: $WORKDIR"دمج الوضع الصارم مع اعتراض الأخطاء
يخبر الوضع الصارم Bash متى يتوقف. ويتيح لك trap على ERR تنفيذ التنظيف أو التشخيص قبل خروج البرنامج النصي.
النمط الشائع هو:
- ضبط الوضع الصارم في البداية
- تعريف دالة باسم
cleanupأوon_error - تسجيلها باستخدام
trap 'on_error' ERR - اختياريًا، اعتراض
EXITأيضًا لضمان التنظيف بغض النظر عن النجاح أو الفشل
مهم: استخدم set -E (حرف E كبير، ويُسمى أيضًا errtrace) لكي يرث اعتراض ERR أيضًا من الدوال والصدفات الفرعية — فمن دونه لا تعمل الاعتراضات إلا في جسم الصَدَفة الرئيسية.
#!/usr/bin/env bash
set -Eeuo pipefail
on_error() {
local exit_code=$?
local line_number=${BASH_LINENO[0]}
echo "ERROR: command failed with code ${exit_code} at line ${line_number}" >&2
}
cleanup() {
echo "Cleaning up temporary files..." >&2
rm -rf /tmp/my_run_dir 2>/dev/null || true
}
trap on_error ERR
trap cleanup EXIT
mkdir -p /tmp/my_run_dir
echo "hello" > /tmp/my_run_dir/output.txt
cat /tmp/my_run_dir/output.txt
echo "Done"تعطيل الوضع الصارم محليًا
قد تكون كتلة من الشيفرة «فوضوية» عن قصد أحيانًا — مثل البحث عن أدوات اختيارية أو تشغيل أوامر قديمة تعيد قيمة غير صفرية لأسباب لا تدل على وجود خطأ. يمكنك تعطيل الوضع الصارم مؤقتًا ثم استعادته بعد ذلك.
النمط الآمن:
- احفظ الحالة باستخدام
set +e(يعطّل-e)، وشغّل الكتلة، ثم أعد التفعيل باستخدامset -e - أو استخدم صدفة فرعية
( set +e; ... )حتى لا تتأثر رايات الصَدَفة الأصلية مطلقًا - أعد تفعيل الرايات دائمًا فور انتهاء الكتلة الخطرة — فترك الرايات معطلة سبب شائع للأخطاء
فضّل استخدام الصَدَفة الفرعية عندما تتضمن الكتلة أوامر متعددة، إذ تستعيد الرايات تلقائيًا عند الخروج.
#!/usr/bin/env bash
set -euo pipefail
# Probe for optional tools without aborting
HAS_JQ=false
(
set +e
command -v jq > /dev/null 2>&1
[[ $? -eq 0 ]] && echo "jq_found"
) && HAS_JQ=true || true
if [[ "$HAS_JQ" == "true" ]]; then
echo "jq is available — using JSON output"
else
echo "jq not found — using plain text"
fiقالب متكامل لبرنامج نصي بالوضع الصارم
إليك قالبًا جاهزًا للاستخدام في الإنتاج يجمع أفضل ممارسات الوضع الصارم التي تناولها هذا الدرس:
set -Eeuo pipefail— الرايات الأربع جميعها، بما في ذلكerrtraceIFS=$'\n\t'— تقسيم كلمات أكثر أمانًا (يتجنب التقسيم عند المسافات)- اعتراضا ERR وEXIT للتشخيص والتنظيف
- قيم افتراضية صريحة للمعلمات الاختيارية
readonlyوlocalللحد من نطاق المتغيرات
انسخ هذا القالب في بداية كل برنامج نصي غير بسيط في Bash للاستفادة فورًا من سلوك الإيقاف عند أول فشل ومن الأخطاء القابلة للتتبع.
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
# ── Constants ────────────────────────────────────────────
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
# ── Trap handlers ────────────────────────────────────────
err_handler() {
echo "[${SCRIPT_NAME}] ERROR on line ${BASH_LINENO[0]}: exit ${?}" >&2
}
cleanup() {
echo "[${SCRIPT_NAME}] Exiting" >&2
}
trap err_handler ERR
trap cleanup EXIT
# ── Defaults ─────────────────────────────────────────────
ENV="${1:-production}"
MAX_RETRIES="${MAX_RETRIES:-3}"
# ── Main ─────────────────────────────────────────────────
main() {
echo "Running in env=${ENV}, max_retries=${MAX_RETRIES}"
echo "Script dir: ${SCRIPT_DIR}"
}
main "$@"اختبار المعرفة: سلوك pipefail
اختبر مدى فهمكم لكيفية تأثير pipefail في رموز خروج مسارات الأوامر.
مراجعة: الوضع الصارم باستخدام set -euo pipefail
تعلّمتم في هذا الدرس كيفية جعل نصوص Bash تتوقف سريعًا وتُظهر الأخطاء بوضوح باستخدام الوضع الصارم.
الخيارات الثلاثة وما الذي تحمي منه:
-e(errexit) — يخرج عند إرجاع أي أمر لحالة غير صفرية؛ ويُستثنى من ذلك استخدامه في الشروط وبعد||-u(nounset) — يوقف التنفيذ عند الإشارة إلى متغير غير معرّف؛ استخدموا${VAR:-default}للمتغيرات الاختيارية-o pipefail— يجعل مسار الأوامر بأكمله يفشل إذا فشلت أي مرحلة فيه، وليس المرحلة الأخيرة فقط
ممارسات مكملة:
- أضيفوا
-E(errtrace) لكي تنتقل مصائد ERR إلى الدوال - استخدموا
trapمعERRوEXITلأغراض التشخيص والتنظيف - استخدموا
|| trueللسماح بالفشل عمدًا - عطّلوا الوضع مؤقتًا باستخدام
set +eداخل الصدَفات الفرعية عند التعامل مع نصوص قديمة أو نصوص الفحص
الوضع الصارم ليس حلًا سحريًا — لذا تعرّفوا على استثناءاته — لكنه يظل العادة الأكثر فاعلية لكتابة نصوص Bash موثوقة ودفاعية.
الأسئلة الشائعة
هل درس «الوضع الصارم باستخدام set -euo pipefail» مجاني؟
نعم — نص درس «الوضع الصارم باستخدام set -euo pipefail» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Linux Command Line & Bash Scripting Mastery، انتقل إلى CoddyKit PRO. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
ماذا ستتعلم في «الوضع الصارم باستخدام set -euo pipefail»؟
فعّل سلوك التوقّف عند الفشل وافهم بدقة الأخطاء التي يلتقطها كل خيار من خيارات الوضع الصارم والتي لا يلتقطها تتمرن على Linux Command Line & Bash Scripting Mastery مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Linux Command Line & Bash Scripting Mastery؟
لا تُشترط خبرة سابقة. Linux Command Line & Bash Scripting Mastery على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «الوضع الصارم باستخدام set -euo pipefail»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Linux Command Line & Bash Scripting Mastery هذا؟
نعم. كل درس في Linux Command Line & Bash Scripting Mastery يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الوضع الصارم باستخدام set -euo pipefail
- معالجات trap للتنظيف والإشارات
- الملفات المؤقتة الآمنة ومجلدات القفل
- السكربتات القابلة للتكرار ومنطق إعادة المحاولة بالتراجع التدريجي