معالجات trap للتنظيف والإشارات
سجّل مصائد EXIT وERR وINT لحذف الملفات المؤقتة والتراجع عن الأعمال الجزئية بشكل موثوق
معالجات trap للتنظيف والإشارات درس مجاني في Linux Command Line & Bash Scripting Mastery على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Linux Command Line & Bash Scripting Mastery، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
أهمية معالجات المصائد
عندما يخرج نص Bash — سواء بصورة طبيعية أو بسبب خطأ أو لأن المستخدم ضغط Ctrl+C — فقد يترك وراءه ملفات مؤقتة أو بيانات مكتوبة جزئيًا أو حالة تالفة. ومن دون التنظيف، تتراكم هذه الآثار وتتسبب في مشكلات يصعب تصحيحها.
تحل معالجات المصائد هذه المشكلة من خلال تسجيل دالة أو أمر ينفذه Bash تلقائيًا عند تلقي إشارة محددة أو إشارة زائفة.
- EXIT — يُنفَّذ كلما خرج النص، لأي سبب كان
- ERR — يُنفَّذ بعد أن يُرجع أي أمر حالة خروج غير صفرية
- INT — يُنفَّذ عندما يرسل المستخدم SIGINT (Ctrl+C)
- TERM — يُنفَّذ عند وصول SIGTERM (مثلًا من
kill)
يسجّل النص المكتوب جيدًا هذه المصائد في بدايته تمامًا، قبل بدء أي عمل قد ينطوي على مخاطر.
بنية الأمر المضمّن trap
يسجّل الأمر المضمّن trap معالجًا لإشارة واحدة أو أكثر، سواء أكانت إشارات أم إشارات زائفة. والبنية العامة هي:
trap 'command_or_function' SIGNAL [SIGNAL...]
القواعد الأساسية:
- الوسيط الأول هو سلسلة مقتبسة من تعليمات shell (أو اسم دالة) لتنفيذها عند إطلاق الإشارة.
- يمكنكم إدراج إشارات متعددة بعد المعالج.
trap '' SIGNALيتجاهل تلك الإشارة (معالج فارغ).trap - SIGNALيعيد ضبط الإشارة إلى سلوكها الافتراضي.
ترث الدوال المصائد عند استدعائها في الصدفة نفسها، لكن الصدفات الفرعية التي يتم إنشاؤها باستخدام ( ) أو & لا ترثها.
#!/usr/bin/env bash
# Syntax examples — not a full script
# Register a cleanup function on EXIT
trap cleanup EXIT
# Inline handler for INT and TERM
trap 'echo "Interrupted!"; exit 1' INT TERM
# Ignore SIGHUP
trap '' HUP
# Reset SIGPIPE to default
trap - PIPEمصيدة EXIT — شبكة الأمان الخاصة بكم
تُعد الإشارة الزائفة EXIT أهم مصيدة ينبغي تسجيلها. فهي تُنفَّذ عند خروج الصدفة لأي سبب: الإكمال الطبيعي، أو exit N، أو خطأ غير معالج، أو إشارة. لذلك فهي مثالية لإزالة الملفات المؤقتة.
أفضل الممارسات:
- أنشئوا الملفات المؤقتة باستخدام
mktempكي يكون اسمها فريدًا وغير قابل للتنبؤ. - خزّنوا المسار في متغير فورًا.
- سجّلوا مصيدة EXIT مباشرة بعد الإنشاء لضمان تنفيذ التنظيف دائمًا.
يُحفَظ رمز خروج النص — ويُتجاهل رمز خروج معالج المصيدة إلا إذا استدعيتم exit صراحةً بداخله.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/myapp.XXXXXX)
cleanup() {
echo "Removing temp file: $TMPFILE" >&2
rm -f "$TMPFILE"
}
trap cleanup EXIT
# Do real work
echo "Processing..." > "$TMPFILE"
sort "$TMPFILE" -o "$TMPFILE"
cp "$TMPFILE" /tmp/myapp_result.txt
echo "Done. Result saved."التعامل مع ملفات ومجلدات مؤقتة متعددة
تنشئ النصوص الواقعية غالبًا عدة عناصر مؤقتة — ملفات ومجلدات وFIFOs وملفات قفل. وبدلًا من تتبع كل عنصر على حدة، اجمعوها في مصفوفة وأزيلوا كل شيء ضمن دالة تنظيف واحدة.
يتوسع هذا النمط بسهولة: ففي كل مرة تنشئون فيها موردًا مؤقتًا جديدًا، أضيفوه إلى المصفوفة. وستتعامل مصيدة EXIT دائمًا مع القائمة كاملة، بصرف النظر عن الموضع الذي يخرج منه النص.
#!/usr/bin/env bash
set -euo pipefail
CLEANUP_TARGETS=()
add_cleanup() {
CLEANUP_TARGETS+=("$1")
}
cleanup() {
echo "Running cleanup..." >&2
for target in "${CLEANUP_TARGETS[@]:-}"; do
rm -rf "$target"
echo "Removed: $target" >&2
done
}
trap cleanup EXIT
# Create and track temp resources
TMPDIR_WORK=$(mktemp -d /tmp/work.XXXXXX)
add_cleanup "$TMPDIR_WORK"
TMPFILE_LOG=$(mktemp /tmp/run.XXXXXX.log)
add_cleanup "$TMPFILE_LOG"
echo "Working in $TMPDIR_WORK" | tee "$TMPFILE_LOG"
touch "$TMPDIR_WORK/output.txt"
echo "Step complete."مصيدة ERR — التقاط حالات الفشل
تُنفَّذ الإشارة الزائفة ERR بعد أن يُرجع أي أمر بسيط حالة خروج غير صفرية، بشرط أن يكون خيار الصدفة set -e (errexit) مفعّلًا، أو ألا يكون الأمر داخل بنية مركبة من نوع if أو while أو ||/&&.
استخدموا مصيدة ERR من أجل:
- تسجيل السطر الذي حدث فيه الفشل باستخدام
$LINENOو$BASH_COMMAND. - بدء منطق التراجع قبل تنفيذ مصيدة EXIT.
- إرسال رسائل خطأ منظمة إلى stderr.
مهم: لا تُنفَّذ مصائد ERR داخل الدوال إلا إذا شغّلتم صراحةً set -E (errtrace) لكي ترث الدوال مصيدة ERR من الصدفة المستدعية.
#!/usr/bin/env bash
set -eEuo pipefail
on_error() {
local exit_code=$?
local line=$1
echo "ERROR: command failed with exit code $exit_code at line $line" >&2
echo " Command: $BASH_COMMAND" >&2
}
trap 'on_error $LINENO' ERR
echo "Starting task..."
cp /nonexistent/file /tmp/ # This will fail
echo "This line is never reached."مصيدة INT — التعامل السلس مع Ctrl+C
يؤدي الضغط على Ctrl+C إلى إرسال SIGINT إلى مجموعة العمليات الموجودة في الواجهة. ومن دون مصيدة، يخرج Bash فورًا، وقد يترك عملًا جزئيًا وراءه.
يتيح لكم تسجيل مصيدة INT ما يلي:
- طباعة رسالة إلغاء واضحة وملائمة للمستخدم.
- تنفيذ أي تراجع مرحلي قبل تمرير التنفيذ إلى مصيدة EXIT.
- الخروج برمز حالة ذي معنى (يُعد
130الرمز الاصطلاحي لـ SIGINT:128 + 2).
بعد انتهاء معالج INT، استدعوا exit 130 لتمرير رمز الخروج الصحيح. وستُنفَّذ مصيدة EXIT تلقائيًا بعد ذلك.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/demo.XXXXXX)
cleanup() {
rm -f "$TMPFILE"
echo "Temp file removed." >&2
}
on_interrupt() {
echo "" >&2
echo "Caught SIGINT — cancelling gracefully." >&2
exit 130 # EXIT trap (cleanup) runs automatically after this
}
trap cleanup EXIT
trap on_interrupt INT
echo "Running long task. Press Ctrl+C to cancel."
for i in $(seq 1 10); do
echo "Step $i/10..."
sleep 1
done
echo "All steps complete."دمج مصائد EXIT وERR وINT
في النصوص المستخدمة في بيئة الإنتاج، تسجّلون عادةً المصائد الثلاث كلها معًا لتغطية كل مسارات الخروج:
- EXIT — تُنفَّذ دائمًا وتتولى تنظيف الملفات.
- ERR — تسجّل الأمر الذي فشل ورقم السطر.
- INT — تطبع رسالة إلغاء وتخرج بالرمز 130.
نظرًا إلى أن EXIT تُنفَّذ دائمًا في النهاية، فلا يحتاج منطق التنظيف إلى أن يكون إلا في موضع واحد. ويمكن لمعالجي ERR وINT التركيز على التسجيل وتعيين الحالة، ثم تفويض التنظيف الفعلي إلى EXIT من خلال استدعاء exit.
استخدموا set -eEuo pipefail في البداية لكي تؤدي الأخطاء في الدوال أيضًا إلى تشغيل ERR، ولكي تلتقط pipefail حالات الفشل داخل مسارات الأوامر.
#!/usr/bin/env bash
set -eEuo pipefail
TMPFILE=$(mktemp /tmp/combined.XXXXXX)
ROLLBACK_NEEDED=false
cleanup() {
if [[ "$ROLLBACK_NEEDED" == true ]]; then
echo "Rolling back partial changes..." >&2
fi
rm -f "$TMPFILE"
echo "Cleanup done." >&2
}
on_error() {
ROLLBACK_NEEDED=true
echo "ERR at line $1: $BASH_COMMAND" >&2
}
on_interrupt() {
echo "Interrupted by user." >&2
exit 130
}
trap cleanup EXIT
trap 'on_error $LINENO' ERR
trap on_interrupt INT
echo "hello" > "$TMPFILE"
echo "Script finished successfully."إعادة ضبط المصائد وتعطيلها ديناميكيًا
قد تحتاجون أحيانًا إلى تعديل مصيدة أثناء تنفيذ النص — مثلًا لإجراء تنظيف لمرة واحدة بعد اكتمال قسم حرج، بحيث لا تؤدي العمليات العادية اللاحقة إلى تراجع غير ضروري.
trap - SIGNALيعيد ضبط الإشارة إلى سلوكها الافتراضي المضمّن.trap '' SIGNALيتجاهل الإشارة تمامًا (ولا يمكن إنهاء العملية بواسطتها).- يمكنكم تسجيل معالج جديد في أي وقت؛ ويكون لاستدعاء
trapالأخير الأولوية.
من الأنماط الشائعة ترقية مصيدة EXIT بعد نجاح عملية محفوفة بالمخاطر، بالتبديل من معالج للتراجع إلى معالج تنظيف بسيط.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/staged.XXXXXX)
rollback() {
echo "ROLLBACK: removing $TMPFILE" >&2
rm -f "$TMPFILE"
}
cleanup_only() {
echo "CLEANUP: removing $TMPFILE" >&2
rm -f "$TMPFILE"
}
# Start with rollback in case something fails during the risky phase
trap rollback EXIT
echo "Performing risky operation..."
echo "critical data" > "$TMPFILE"
# ... imagine more risky steps here ...
# Risky phase succeeded — switch to simple cleanup
trap cleanup_only EXIT
echo "Risky phase done. Now doing safe finalization."
cp "$TMPFILE" /tmp/staged_result.txt
echo "All done."تمرير رموز الخروج عبر المصائد
هناك مشكلة دقيقة ينبغي الانتباه إليها: إذا احتوت دالة التنظيف على أوامر يمكن أن تفشل، فقد يُستبدل رمز الخروج النهائي للنص. احفظوا رمز الخروج الأصلي صراحةً.
داخل معالج المصيدة، يحتوي $? على رمز الخروج الذي أدى إلى تشغيل المصيدة. التقطوا قيمته في السطر الأول من المعالج، قبل أن يغيّرها أي أمر آخر.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/exitcode.XXXXXX)
cleanup() {
local original_exit=$? # capture BEFORE any other command
echo "Cleaning up (original exit: $original_exit)" >&2
rm -f "$TMPFILE"
# Exit with the original code so callers see the real result
exit "$original_exit"
}
trap cleanup EXIT
echo "Doing work..."
echo "data" > "$TMPFILE"
# Simulate failure
if [[ ! -f /tmp/required_marker ]]; then
echo "Required marker missing!" >&2
exit 2
fi
echo "Done."المصائد في الدوال والصدفات الفرعية
يُعد فهم وراثة المصائد أمرًا بالغ الأهمية لكتابة نصوص صحيحة:
- تعمل الدوال في الصدفة نفسها وترث المصائد — لكن ERR لا تنتقل إلى الدوال إلا عند تفعيل
set -E(errtrace). - تبدأ الصدفات الفرعية (
( ... )) بنسخة من مصائد الصدفة الأصلية، لكن التغييرات داخل الصدفة الفرعية لا تؤثر في الصدفة الأصلية. - تُعد مهام الخلفية (
cmd &) صدفات فرعية، ولا ترث مصائد INT/TERM من الصدفة الأصلية (بل تحصل على معالجة الإشارات الافتراضية).
قاعدة عملية: أبقوا تسجيل المصائد في النص الرئيسي، واستخدموا set -E عندما تحتاجون إلى انتقال ERR إلى الدوال، ولا تعتمدوا مطلقًا على وراثة المصائد عبر &.
#!/usr/bin/env bash
set -eEuo pipefail # -E = errtrace: ERR propagates into functions
on_error() {
echo "ERR in function or main at line $1" >&2
}
trap 'on_error $LINENO' ERR
risky_function() {
echo "Inside risky_function"
ls /nonexistent_path # triggers ERR — works because of set -E
}
# Subshell: has its own copy; changes don't affect parent
(
trap '' ERR # disable ERR only inside subshell
ls /nonexistent_path 2>/dev/null || true
echo "Subshell completed without triggering parent ERR"
)
risky_functionنمط واقعي: الاستبدال الذري للملفات
من أنماط Bash الدفاعية الكلاسيكية الاستبدال الذري للملفات: اكتبوا في ملف مؤقت، ثم أعيدوا تسميته إلى المسار المطلوب. فإذا فشل أي شيء قبل إعادة التسمية، يظل الملف الأصلي دون تغيير. وتجعل المصائد هذا النمط محكمًا.
الخطوات:
- أنشئوا ملفًا مؤقتًا في نظام الملفات نفسه الذي يوجد فيه الملف الهدف (لكي يكون
mvذريًا). - سجّلوا مصيدة EXIT لإزالة الملف المؤقت إذا اضطررنا إلى الخروج مبكرًا.
- اكتبوا المحتوى الجديد وتحققوا من صحته.
- أعيدوا التسمية ذريًا (
mv) — عند هذه النقطة فقط يُستبدل الملف الأصلي. - تزيل مصيدة EXIT الملف المؤقت فقط إذا كان لا يزال موجودًا (فبعد نجاح
mvيكون قد اختفى).
#!/usr/bin/env bash
set -euo pipefail
DEST="/tmp/important_config.conf"
TMPFILE=$(mktemp "$(dirname "$DEST")/.tmp.XXXXXX")
cleanup() {
rm -f "$TMPFILE" # no-op if mv already moved it
}
trap cleanup EXIT
# Write new content to temp file
cat > "$TMPFILE" <<'EOF'
[settings]
version=2
mode=production
EOF
# Validate before replacing
if ! grep -q 'version=' "$TMPFILE"; then
echo "Validation failed — aborting replacement" >&2
exit 1
fi
# Atomic rename — DEST is replaced only here
mv "$TMPFILE" "$DEST"
echo "Config updated atomically: $DEST"اختبار المعرفة: وراثة مصيدة ERR
اختبروا مدى فهمكم لسلوك مصيدة ERR مع الدوال في Bash.
مراجعة: معالجات المصائد للتنظيف والإشارات
تعلّمتم في هذا الدرس كيفية جعل نصوص Bash أكثر مرونة باستخدام معالجات المصائد:
- تُعد EXIT شبكة الأمان الشاملة — سجّلوها فورًا بعد إنشاء أي مورد مؤقت، والتقطوا دائمًا
$?أولًا للحفاظ على رمز الخروج الأصلي. - تتيح لكم ERR تسجيل حالات الفشل باستخدام
$LINENOو$BASH_COMMAND؛ استخدمواset -Eلكي ترثها الدوال. - تتعامل INT مع Ctrl+C بسلاسة — اطبعوا رسالة واخرجوا بالرمز 130 لكي تتولى EXIT التنظيف.
- اجمعوا الموارد المؤقتة في مصفوفة وأزيلوها جميعًا ضمن دالة تنظيف واحدة.
- استخدموا
trap - SIGNALلإعادة الضبط، وtrap '' SIGNALلتجاهل الإشارات ديناميكيًا. - يضمن نمط إعادة التسمية الذرية (
mktemp→ write → validate →mv)، بالاقتران مع مصيدة EXIT، ألا تؤدي عمليات الكتابة الجزئية إلى إفساد الملف الهدف.
يوفر الجمع بين set -eEuo pipefail والمصائد الموضوعة بعناية أساسًا دفاعيًا متينًا لأي نص Bash مخصص لبيئة الإنتاج.
الأسئلة الشائعة
هل درس «معالجات trap للتنظيف والإشارات» مجاني؟
نعم — نص درس «معالجات trap للتنظيف والإشارات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Linux Command Line & Bash Scripting Mastery، انتقل إلى CoddyKit PRO. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
ماذا ستتعلم في «معالجات trap للتنظيف والإشارات»؟
سجّل مصائد EXIT وERR وINT لحذف الملفات المؤقتة والتراجع عن الأعمال الجزئية بشكل موثوق تتمرن على Linux Command Line & Bash Scripting Mastery مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Linux Command Line & Bash Scripting Mastery؟
لا تُشترط خبرة سابقة. Linux Command Line & Bash Scripting Mastery على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «معالجات trap للتنظيف والإشارات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Linux Command Line & Bash Scripting Mastery هذا؟
نعم. كل درس في Linux Command Line & Bash Scripting Mastery يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الوضع الصارم باستخدام set -euo pipefail
- معالجات trap للتنظيف والإشارات
- الملفات المؤقتة الآمنة ومجلدات القفل
- السكربتات القابلة للتكرار ومنطق إعادة المحاولة بالتراجع التدريجي