CRLF مقابل LF أخطاء نهاية السطر التي تؤدي إلى توقف أنظمة التحقق التلقائي

نُشرت في

يُمكن تشغيل سكربتك المحلي بشكل صحيح، لكن نظام CI يُظهر "مُفسر غير صالح: /bin/bash^M". هذا هو مشكلة توقف السطر (CRLF). اعرف ما يسببها، وكيف تكتشفها، وكيف تصل إلى حل دائم باستخدام ملف .gitattributes.

CRLF مقابل LF: العطل في نهاية السطر الذي يُسبب فشل CI 1
إعلان · حذف؟

يُمكن تشغيل سكربتك في جهازك، لكن عند رفعه إلى GitHub، يفشل CI مع خطأ غامض. /bin/bash^M: bad interpreter لقد واجهت أنت أخطر عطل في تطوير البرمجيات: خطأ في نهاية السطر.

يشرح هذا الدليل ما هو CRLF وLF بالفعل، ولماذا يُسبب تلفًا سلبيًا عند مزجهما، وما هي الخطوات الدقيقة لمنع عطلات نهاية السطر من أن تصل إلى خططك مرة أخرى.

ما هي CRLF وLF؟

يحتاج كل ملف نصي إلى طريقة لتحديد نهاية السطر. توجد معايير مُرثية من عصر الأجهزة المطبوعة الورقية:

  • LF (Line Feed) — حرف واحد (بайت) \n . 0x0Aيُستخدم في لينكس، مك أوس، وكل أنظمة يونكس.
  • CRLF (Carriage Return + Line Feed) — حرفان، \r\n . 0x0D 0x0Aيُستخدم في ويندوز وMS-DOS.

تستمد الأسماء من ميكانيكية المطبوعات. كان الحرف المُعادل للعودة يُعيد وضع المطبوعة إلى بداية السطر. بينما كان الحرف المُعادل للخط يُحرك الورق إلى صف جديد. حافظ ويندوز على كلا الحرفين، بينما أزال يونكس الحرف المُعادل للعودة. السبب الذي يُسبب تلف CRLF في خطط CI أعاد تدوير رأس الطباعة إلى بداية السطر. أ مُدخل السطر أُدخل ورقة إلى صف واحد. حافظت ويندوز على كلا القيمتين؛ أما يونكس فقد أزالت العلامة المكررة للعودة إلى البداية.

لماذا تُعطل CRLF خطوط البناء في أنظمة CI

في لينكس (حيث تُنفذ تقريبًا جميع مُشغلات CI)، فإن \r ليس مسافات فارغة — بل هو حرف حقيقي. عندما يُحفظ سكربت شيل مع نهاية سطر CRLF، فإن كل سطر ينتهي بـ \r قبل السطر الجديد. يُفسر النص المُحدد من قبل النواة كـ #!/bin/bash\r ويسأل عن برنامج مسمى bash\rالذي لا يوجد.

يبدو الخطأ كالتالي:

: bad interpreter: /bin/bash^M: No such file or directory

ال ^M هو الطريقة التي تُعرض بها حرف العودة في الأجهزة. يُظهرها معظم أدوات النصوص بشكل غير مرئي، وهو ما يجعل هذا العطل مُربكًا جدًا.

مواقع أخرى تُسبب فيها CRLF تلفًا سلبيًا

  • Dockerfiles — سيُدخل أمر RUN إلى كل أمر، مما يُسبب تلفًا في مقارنات النصوص ومسارات الملفات. \r إلى كل أوامر، مما يُسبب تعارضات في مقارنة النصوص ومسارات الملفات.
  • سكربتات بايثونSyntaxError: unexpected character after line continuation character عندما \ متبوعًا بـ \r\n.
  • ملفات .env — تأخذ القيم المتغيرة للبيئة حرفًا خلفيًا \r، لذا فإن APP_ENV=production\r لا تُطابق القيمة المتوقعة production.
  • ملفات CSV وبيانات — قد تُدرج أدوات التحليل التي تُقرأ سطرًا بسطر في الحقل الأخير لكل سطر. \r في الحقل الأخير من كل سطر.
  • مفاتيح SSH — سيُرفض ملف المفتاح المُعد بـ CRLF بشكل سلبي من قبل خادم SSH.
  • مُقارنات Git — يُظهر كل سطر كمُعدّل، مما يُغطي التغييرات الحقيقية بضوضاء.

كيفية اكتشاف مشاكل نهاية السطر

تُخفي معظم المحررات نهاية السطر بشكل افتراضي. إليك الطرق الموثوقة للتحقق منها:

باستخدام cat أو hexdump

# Show ^M characters
cat -A yourfile.sh | head -5

# Hex dump to see 0x0d (CR) characters
hexdump -C yourfile.sh | head -10

باستخدام أمر file

file yourfile.sh
# CRLF output: yourfile.sh: ASCII text, with CRLF line terminators
# LF output:   yourfile.sh: ASCII text

باستخدام grep

# Returns exit code 0 (found) if CRLF endings exist
grep -rlP "\r" . --include="*.sh" --include="*.py" --include="*.yml"

كيفية إصلاح نهاية السطر

الخيار 1: dos2unix (أفضل حل سريع وواحد مرة)

dos2unix تُزيل الحروف المُعادلة للعودة من الملفات. وهي متاحة على جميع أنظمة لينكس الرئيسية:

# Fix a single file
dos2unix yourscript.sh

# Fix all shell scripts recursively
find . -name "*.sh" -exec dos2unix {} \;

# Reverse: convert LF to CRLF (unix2dos)
unix2dos yourfile.sh

الخيار 2: sed (لا حاجة لبرامج إضافية)

# Remove carriage returns in-place
sed -i 's/\r//' yourscript.sh

# Or using tr
tr -d '\r' < input.sh > output.sh

الخيار 3: إصلاح في VS Code أو JetBrains

في VS Code، يُظهر وضع نهاية السطر في شريط الحالة (المنطقة السفلى اليمنى). اضغط عليه لتبديل بين CRLF وLF للملف الحالي. لتغيير القيمة الافتراضية للملفات الجديدة، قم بتعيين "files.eol": "\n" في ملف settings.json.

في IDEs من JetBrains، اذهب إلى ملف → نهاية السطر لتعديل الملف الحالي، أو قم بتعيين القيمة الافتراضية في مُحرر → نمط الكود → نهاية السطر.

الحل الصحيح: ملف .gitattributes

لا يُمكن التوسع على الحلات الفردية. الحل الصحيح هو ملف .gitattributes يُخبر Git بالضبط ما هي نهاية السطر المطلوبة، بغض النظر عن ما يستخدمه محرر أو نظام التشغيل.

# .gitattributes — commit this to the root of your repository

# Default: normalize all text files to LF in the repo
* text=auto eol=lf

# Explicitly enforce LF for scripts and configs
*.sh    text eol=lf
*.bash  text eol=lf
*.py    text eol=lf
*.rb    text eol=lf
*.yml   text eol=lf
*.yaml  text eol=lf
*.json  text eol=lf
*.env   text eol=lf
Dockerfile text eol=lf
Makefile   text eol=lf

# Windows-only files can keep CRLF
*.bat  text eol=crlf
*.cmd  text eol=crlf
*.ps1  text eol=crlf

# Binary files — never touch line endings
*.png  binary
*.jpg  binary
*.gif  binary
*.zip  binary
*.pdf  binary

بعد إضافة هذا الملف، قم بتشغيل ما يلي لتنموذج كل مخزن في خطوة واحدة:

git add --renormalize .
git commit -m "chore: normalize line endings via .gitattributes"

إعداد Git autocrlf — ولماذا يُسبب تدهورًا متكررًا

يحتوي Git على إعداد core.autocrlf يحاول تحويل نهاية السطر تلقائيًا:

  • core.autocrlf=true — يُحول LF إلى CRLF عند التحقق (ويندوز)، و CRLF إلى LF عند التسجيل. مُخصص للأشخاص في ويندوز.
  • core.autocrlf=input — يُحول CRLF إلى LF عند التسجيل، ولا يفعل أي شيء عند التحقق. أكثر أمانًا لـ مك أو لينكس.
  • core.autocrlf=false — لا يفعل أي شيء. ما يُحفظه المحرر يُُسجل.

المشكلة: core.autocrlf هو إعداد محلي مُخزن في كل مطور في فريقك لديه قيمة مختلفة، لذا فإن التسجيلات من أجهزة مختلفة تُنتج نهاية سطر مختلفة. هذا يُسبب ضوضاء مستمرة في المقارنات وفشل CI متقطع حسب من أحدث الملف. setting stored in ~/.gitconfig. كل مطوّر في فريقك يمتلك قيمة مختلفة، لذا فإن التحديثات من أجهزة مختلفة تُنتج أطرافًا مختلفة. هذا يُسبب ضوضاء مستمرة في التغييرات وفشل أنظمة CI متقطع وفقًا لمن أحدث الملف أخيرًا.

مبدأ عام: استخدم .gitattributes لتحديد سياسة نهاية السطر في المخزن. اجعل core.autocrlf مُساوٍ لقيمة كل مطور — .gitattributes يُغلقها.

إضافة حماية في CI

حتى مع وجود .gitattributes في المكان، من المهم إضافة تحقق صريح في CI للكشف عن أي ملف يُفوّت. خطوة بسيطة تغطي معظم الحالات:

# In your CI workflow (GitHub Actions example)
- name: Check for CRLF line endings
  run: |
    if grep -rlP "\r" . --include="*.sh" --include="*.py" --include="*.yml" --include="Dockerfile"; then
      echo "ERROR: CRLF line endings found. Run dos2unix on the above files."
      exit 1
    fi

تُفشل هذه الخطوة بشكل واضح في مصدرها — الطلب — بدلًا من أن تفشل بشكل سلبي عند التسليم.

مراجع سريعة: CRLF مقابل LF

LF (\n)CRLF (\r\n)
البُايتس0x0A0x0D 0x0A
يُستخدم فيلينكس، مك أوس، يونكسويندوز، مس دوس
مُستقر لسكربتات الشيلنعملا — يُسبب تلفًا في مُحدد المُفسر
مُستقر لـ Dockerfileنعملا
مُستقر لملفات .envنعملا — يضيف حرفًا خلفيًا إلى القيم
مُوصى به من Gitيُعاد تطبيعه إلى LF داخل المخزنمُخصص فقط لـ .bat/.cmd/.ps1
هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟