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

الملفات المؤقتة الآمنة ومجلدات القفل

استخدم mktemp وflock لإنشاء موارد مؤقتة خالية من حالات التسابق ومنع تشغيل السكربتات المتزامنة

الملفات المؤقتة الآمنة ومجلدات القفل درس مجاني في Linux Command Line & Bash Scripting Mastery على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Linux Command Line & Bash Scripting Mastery، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Linux Command Line & Bash Scripting Mastery 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) وفتح باقي دورة Linux Command Line & Bash Scripting Mastery، انتقل إلى CoddyKit PRO. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.

ماذا ستتعلم في «الملفات المؤقتة الآمنة ومجلدات القفل»؟

استخدم mktemp وflock لإنشاء موارد مؤقتة خالية من حالات التسابق ومنع تشغيل السكربتات المتزامنة تتمرن على Linux Command Line & Bash Scripting Mastery مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Linux Command Line & Bash Scripting Mastery؟

لا تُشترط خبرة سابقة. Linux Command Line & Bash Scripting Mastery على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «الملفات المؤقتة الآمنة ومجلدات القفل»؟

معظم دروس 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