0Pricing
Linux Command Line & Bash Scripting Mastery · درس

الوضع الصارم باستخدام 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 — الرايات الأربع جميعها، بما في ذلك errtrace
  • IFS=$'\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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الوضع الصارم باستخدام set -euo pipefail
  2. معالجات trap للتنظيف والإشارات
  3. الملفات المؤقتة الآمنة ومجلدات القفل
  4. السكربتات القابلة للتكرار ومنطق إعادة المحاولة بالتراجع التدريجي
← العودة إلى Linux Command Line & Bash Scripting Mastery