الملفات المؤقتة الآمنة ومجلدات القفل
استخدم mktemp وflock لإنشاء موارد مؤقتة خالية من حالات التسابق ومنع تشغيل السكربتات المتزامنة
الملفات المؤقتة الآمنة ومجلدات القفل درس مجاني في DevOps Bootcamp على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في DevOps Bootcamp، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.
لماذا تشكّل الملفات المؤقتة خطرًا أمنيًا
تحتاج نصوص Bash كثيرًا إلى تخزين مؤقت — مثل النتائج الوسيطة وعلامات القفل ومناطق التجهيز. لكن إنشاء الملفات المؤقتة بلا عناية يفتح ثغرات خطيرة.
- حالات التسابق: يمكن لعملية أخرى توقّع اسم الملف وإنشائه أولًا، مما يعيد توجيه عمليات الكتابة الخاصة بكم.
- هجمات الروابط الرمزية: ينشئ المهاجم رابطًا رمزيًا في المسار المتوقع، ويجعله يشير إلى ملف حساس مثل
/etc/passwd. - الملفات المتبقية: إذا تعطل النص، تتراكم الملفات المؤقتة وقد تكشف بيانات حساسة.
الأداتان الأساسيتان اللتان تزيلان هذه المشكلات هما mktemp وflock. يوضح لكم هذا الدرس كيفية استخدام كلتيهما بأمان وبأسلوب دفاعي.
إنشاء ملفات مؤقتة آمنة باستخدام mktemp
ينشئ mktemp ملفًا مؤقتًا باسم عشوائي لا يمكن التنبؤ به، ويعيد مساره. وينشئ الملف ذريًا، لذلك لا تستطيع أي عملية أخرى الاستيلاء على الاسم أولًا.
- البنية:
mktemp [TEMPLATE]— يجب أن ينتهي القالب بثلاثة أحرفXعلى الأقل. - يُستبدل كل حرف
Xبحرف عشوائي، لينتج اسمًا فريدًا مثل/tmp/script.aB3kQz. - يُنشأ الملف تلقائيًا بالأذونات
0600(قابلًا للقراءة من المالك فقط).
احفظوا دائمًا المسار المُعاد في متغير فورًا، لكي تتمكنوا من استخدامه وتنظيفه لاحقًا.
#!/usr/bin/env bash
set -euo pipefail
# Create a secure temp file
TMPFILE=$(mktemp /tmp/myapp.XXXXXX)
echo "Temp file created at: $TMPFILE"
# Write data to it
echo "some intermediate result" > "$TMPFILE"
# Read it back
cat "$TMPFILE"
# Clean up
rm -f "$TMPFILE"احرصوا دائمًا على التنظيف باستخدام trap
إذا خرج النص بشكل غير متوقع — بسبب خطأ أو إشارة أو تشغيل set -e — فستُترك الملفات المؤقتة وراءه ما لم تسجّلوا معالج تنظيف.
ينفّذ الأمر المضمّن trap أمرًا عندما تتلقى الصدفة إشارة أو تخرج. والنمط المعتمد لتنظيف الملفات المؤقتة هو:
- سجّلوا المصيدة فورًا بعد إنشاء الملف المؤقت.
- استخدموا مصيدة
EXITلكي يُنفَّذ التنظيف عند الخروج الطبيعي وغير الطبيعي على حد سواء. - استخدموا أيضًا مصيدتي
INTوTERMإذا كان النص يستغرق وقتًا طويلًا أو كان تفاعليًا.
يضمن ذلك عدم بقاء ملفات يتيمة، حتى إذا أُنهي النص أثناء التنفيذ.
#!/usr/bin/env bash
set -euo pipefail
TMPFILE=$(mktemp /tmp/report.XXXXXX)
# Register cleanup before doing any real work
cleanup() {
rm -f "$TMPFILE"
echo "Cleaned up $TMPFILE" >&2
}
trap cleanup EXIT
# Do work — even if this fails, cleanup() will run
echo "Processing..." > "$TMPFILE"
grep "result" "$TMPFILE" || true
echo "Done. File will be removed on exit."إنشاء أدلة مؤقتة باستخدام mktemp -d
قد تحتاج أحيانًا إلى دليل كامل لتجهيز عدة ملفات، مثل إنشاء أرشيف أو استخراج tarball قبل معالجته. استخدم mktemp -d لإنشاء دليل مؤقت آمن.
- يُنشأ الدليل بأذونات
0700(وصول المالك فقط). - نظّف الدليل باستخدام
rm -rfداخلtrap— واحرص على إزالة محتوى المتغير فقط، وليس مسارًا مكتوبًا بشكل ثابت. - استخدم علامات الاقتباس المزدوجة وتحقق من أن المتغير غير فارغ قبل استدعاء
rm -rfكإجراء أمان إضافي.
#!/usr/bin/env bash
set -euo pipefail
TMPDIR=$(mktemp -d /tmp/extract.XXXXXX)
cleanup() {
# Guard: only rm if variable is set and non-empty
[[ -n "${TMPDIR:-}" ]] && rm -rf "$TMPDIR"
}
trap cleanup EXIT
echo "Working in $TMPDIR"
# Simulate staging files
echo "file one" > "$TMPDIR/part1.txt"
echo "file two" > "$TMPDIR/part2.txt"
ls "$TMPDIR"
echo "All done."مشكلة تشغيل البرنامج النصي بالتزامن
يمكن لمهام Cron ومؤقتات systemd والبرامج النصية التي تُشغَّل يدويًا تشغيل عدة مثيلات من البرنامج النصي نفسه في الوقت ذاته بسهولة. ويؤدي ذلك إلى:
- المعالجة المكررة: معالجة سجلات قاعدة البيانات أو الملفات نفسها مرتين.
- إخراج تالف: كتابة مثيلين في ملف الإخراج نفسه بالتزامن.
- حالات توقف تام أو حالة جزئية: تعديل المثيلين للموارد المشتركة بترتيب متداخل وغير متوقع.
كان الحل التقليدي هو كتابة ملف PID والتحقق منه عند بدء التشغيل، لكن هذا الأسلوب يتضمن نافذة سباق بين التحقق والكتابة. أما الحل الحديث الصحيح فهو flock، الذي يستخدم آلية القفل الإرشادية في النواة لضمان قفل خالٍ من حالات السباق.
القفل باستخدام flock: نمط السطر الواحد
يحصل flock على قفل إرشادي على واصف ملف قبل تشغيل أمر. وأبسط استخدام له هو تغليف البرنامج النصي بالكامل من سطر الأوامر:
flock -n /var/lock/myscript.lock bash myscript.sh
-n(غير حاجب): يخرج فورًا بالحالة 1 إذا كان القفل مستخدمًا بالفعل، بدلًا من الانتظار.- من دون
-n، يحجبflockالتنفيذ حتى يصبح القفل متاحًا — وهذا مفيد لوضع المهام في قائمة انتظار. - ملف القفل نفسه مجرد علامة؛ ولا يهم محتواه. ومن الآمن إبقاؤه بين عمليات التشغيل.
- عند خروج العملية التي تحمل القفل، تحرره النواة تلقائيًا — ولا حاجة إلى تنظيف يدوي.
#!/usr/bin/env bash
# launcher.sh — prevents concurrent runs of worker.sh
set -euo pipefail
LOCKFILE="/tmp/myworker.lock"
if ! flock -n "$LOCKFILE" bash -c 'echo "Running worker..."; sleep 2; echo "Done."'; then
echo "Another instance is already running. Exiting." >&2
exit 1
fiاستخدام flock داخل برنامج نصي باستخدام واصف ملف
لإجراء القفل داخل البرنامج النصي بدلًا من تغليفه من الخارج، استخدم exec لفتح واصف ملف، ثم استدعِ flock على ذلك الواصف. هذا هو النمط الاصطلاحي المستخدم في البرامج النصية المخصصة لبيئات الإنتاج.
exec 200>"$LOCKFILE"يفتح الملف على الواصف 200 للكتابة (وينشئه إذا لزم الأمر).flock -n 200يحاول قفل الواصف 200 بطريقة غير حاجبة.- لأن القفل مرتبط بواصف الملف (وليس باسم الملف)، فإنه يتحرر تلقائيًا عند خروج عملية الصدفة.
- تُستخدم أرقام الواصفات 200-299 اصطلاحيًا لتجنب التعارض مع stdin/stdout/stderr.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/myjob.lock"
# Open lock file on FD 200
exec 200>"$LOCKFILE"
# Attempt non-blocking lock
if ! flock -n 200; then
echo "ERROR: Another instance of this script is running." >&2
exit 1
fi
echo "Lock acquired. Starting work..."
sleep 1
echo "Work complete. Lock will be released on exit."دمج mktemp وflock في برنامج نصي واحد
تحتاج البرامج النصية الدفاعية الحقيقية إلى الأمرين معًا: قفل لمنع التشغيل المتزامن، وملفات مؤقتة آمنة للبيانات الوسيطة. إليك النمط الكامل الذي يجمع بين التقنيتين:
- احصل على القفل أولًا — قبل إنشاء أي ملفات مؤقتة — بحيث لا ينفذ أي عمل سوى مثيل واحد.
- أنشئ الموارد المؤقتة بعد التأكد من الحصول على القفل.
- سجّل
trapفورًا بعد إنشاء الموارد المؤقتة، لضمان التنظيف بغض النظر عن طريقة خروج البرنامج النصي. - لا تضع ملف القفل في الدليل المؤقت — إذ يجب أن يبقى بين عمليات التشغيل لكي يتمكن
flockمن الرجوع إليه.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/report_builder.lock"
exec 200>"$LOCKFILE"
if ! flock -n 200; then
echo "Already running — aborting." >&2
exit 1
fi
# Now safe to create temp resources
TMPDIR=$(mktemp -d /tmp/report.XXXXXX)
TMPLOG=$(mktemp /tmp/report_log.XXXXXX)
cleanup() {
rm -rf "${TMPDIR:-}"
rm -f "${TMPLOG:-}"
}
trap cleanup EXIT
echo "Building report in $TMPDIR" | tee "$TMPLOG"
echo "Step 1 complete" >> "$TMPLOG"
cat "$TMPLOG"قفل الأدلة كآلية بديلة للقفل
في الأنظمة التي لا يتوفر فيها flock (مثل بعض الأنظمة المضمنة أو أنظمة الملفات الشبكية مثل NFS)، يمكنك استخدام أدلة القفل بدلًا منه. إن mkdir عملية ذرية في أنظمة POSIX: ولا تنجح إلا إذا لم يكن الدليل موجودًا مسبقًا.
- أنشئ دليل القفل باستخدام
mkdir /tmp/myscript.lock.d— فإذا كان مثيل آخر قد أنشأه، يفشلmkdirفورًا. - خزّن البيانات الوصفية (مثل PID) داخل الدليل لأغراض التشخيص.
- أزل الدليل دائمًا داخل
trapعندEXIT. - تنبيه: بخلاف
flock، لا يُحرَّر قفل الدليل تلقائيًا إذا قُتلت العملية باستخدام-9أو أُعيد تشغيل الجهاز — لذا أضف فحصًا لاكتشاف الأقفال القديمة.
#!/usr/bin/env bash
set -euo pipefail
LOCKDIR="/tmp/myscript.lock.d"
# Atomic mkdir — fails if directory already exists
if ! mkdir "$LOCKDIR" 2>/dev/null; then
# Check if the holding PID is still alive
HOLDER_PID=$(cat "$LOCKDIR/pid" 2>/dev/null || echo "")
if [[ -n "$HOLDER_PID" ]] && kill -0 "$HOLDER_PID" 2>/dev/null; then
echo "Locked by PID $HOLDER_PID. Exiting." >&2
exit 1
else
echo "Stale lock detected. Removing and continuing." >&2
rm -rf "$LOCKDIR"
mkdir "$LOCKDIR"
fi
fi
echo "$$" > "$LOCKDIR/pid"
trap 'rm -rf "$LOCKDIR"' EXIT
echo "Lock acquired via directory. Running..."
sleep 1
echo "Done."الانتظار المستند إلى مهلة زمنية باستخدام flock
قد ترغب أحيانًا في انتظار القفل بدلًا من الفشل فورًا، لكن من دون الانتظار إلى ما لا نهاية. يدعم flock مهلة زمنية باستخدام الخيار -w.
flock -w 10 200ينتظر القفل مدة تصل إلى 10 ثوانٍ، ثم يخرج بالحالة 1 إذا ظل غير متاح.- هذا مناسب للبرامج النصية التي ينبغي أن تنتظر خلف عملية سابقة قصيرة المدة، لكنها تتخلى عن الانتظار إذا كانت العملية السابقة عالقة.
- اجمع بين
-wورسالة خطأ واضحة تتضمن السياق — مسار ملف القفل ومدة الانتظار — حتى يتمكن المشغّلون من تشخيص حالات التعليق بسرعة.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/data_sync.lock"
TIMEOUT=15
exec 200>"$LOCKFILE"
echo "Waiting up to ${TIMEOUT}s for lock on $LOCKFILE..."
if ! flock -w "$TIMEOUT" 200; then
echo "ERROR: Could not acquire lock after ${TIMEOUT}s." \
"Another instance may be hung." >&2
exit 1
fi
echo "Lock acquired. Syncing data..."
sleep 1
echo "Sync complete."قائمة تحقق دفاعية: الموارد المؤقتة الآمنة
قبل نشر أي برنامج نصي يستخدم ملفات مؤقتة أو أقفالًا، راجع قائمة التحقق التالية:
- استخدم
mktemp، ولا تستخدم مسارات مكتوبة بشكل ثابت — إذ إن/tmp/myapp.tmpقابل للتوقع والاستغلال. - التقط المسار فورًا — استخدم
TMPFILE=$(mktemp ...)قبل أي أمر آخر. - سجّل
trap cleanup EXITفورًا بعد الإنشاء — وليس في نهاية البرنامج النصي. - ضع علامات اقتباس مزدوجة حول جميع استخدامات المتغيرات — مثل
rm -f "$TMPFILE"، وليسrm -f $TMPFILE. - فضّل
flockعلى ملفات PID — فهو مُدار من النواة ويُحرَّر تلقائيًا عند حدوث عطل. - استخدم
-nغير الحاجب افتراضيًا — فالأقفال الحاجبة بصمت تخفي مشكلات الأداء. - ضع ملف القفل خارج الدليل المؤقت — حتى يبقى بعد تنفيذ
trapالخاص بالتنظيف. - اختبر سلوك التنظيف — شغّل البرنامج النصي ثم نفّذ
kill -9أثناء تشغيله؛ وتحقق من عدم بقاء أي ملفات (في البرامج النصية المستندة إلىflock؛ أما أقفال الأدلة فتحتاج إلى عناية إضافية).
اختبار المعرفة: سلوك خيارات flock
تعمل مهمة Cron كل دقيقة وتعالج ملفًا مشتركًا. تريد أن تخرج أي عملية استدعاء جديدة فورًا مع ظهور خطأ إذا كانت عملية سابقة لا تزال نشطة، من دون انتظار. أي استدعاء لـ flock يحقق ذلك بشكل صحيح؟
مراجعة: الملفات المؤقتة الآمنة وأدلة القفل
تعلمت في هذا الدرس الأداتين الأساسيتين لإدارة الموارد بطريقة دفاعية في Bash:
mktempينشئ ملفات مؤقتة بأذونات آمنة وغير متوقعة (0600) وأدلة مؤقتة (0700)، مما يلغي حالات السباق وهجمات الروابط الرمزية التي تسببها المسارات المكتوبة بشكل ثابت.trap cleanup EXITيضمن إزالة الملفات المؤقتة عند أي خروج — طبيعي أو ناتج عن خطأ أو إشارة — عند تسجيله فورًا بعد الإنشاء.flockيوفر قفلًا إرشاديًا تفرضه النواة: استخدم-nللفشل السريع عند التنافس، و-w Nللانتظار مع مهلة زمنية، ونمطexec 200>fileللقفل داخل البرنامج النصي، والذي تحرره النواة تلقائيًا عند خروج العملية.- أدلة القفل (
mkdir) توفر بديلًا قابلًا للنقل في البيئات التي لا يتوفر فيهاflock، لكنها تتطلب اكتشاف الأقفال القديمة صراحةً. - احرص دائمًا على إبقاء ملف القفل خارج الدليل المؤقت، وعلى وضع علامات اقتباس مزدوجة حول كل متغير يُستخدم في التنظيف.
يمنحك الجمع بين mktemp + flock + trap برامج نصية آمنة في مواجهة الاستدعاءات المتزامنة، والأعطال غير المتوقعة، والتلاعب العدائي بنظام الملفات.
الأسئلة الشائعة
هل درس «الملفات المؤقتة الآمنة ومجلدات القفل» مجاني؟
نعم — نص درس «الملفات المؤقتة الآمنة ومجلدات القفل» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة DevOps Bootcamp، انتقل إلى CoddyKit PRO. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.
ماذا ستتعلم في «الملفات المؤقتة الآمنة ومجلدات القفل»؟
استخدم mktemp وflock لإنشاء موارد مؤقتة خالية من حالات التسابق ومنع تشغيل السكربتات المتزامنة تتمرن على DevOps Bootcamp مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ DevOps Bootcamp؟
لا تُشترط خبرة سابقة. DevOps Bootcamp على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «الملفات المؤقتة الآمنة ومجلدات القفل»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس DevOps Bootcamp هذا؟
نعم. كل درس في DevOps Bootcamp يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الوضع الصارم باستخدام set -euo pipefail
- معالجات trap للتنظيف والإشارات
- الملفات المؤقتة الآمنة ومجلدات القفل
- السكربتات القابلة للتكرار ومنطق إعادة المحاولة بالتراجع التدريجي