CRLF مقابل LF أخطاء نهاية السطر التي تؤدي إلى توقف أنظمة التحقق التلقائي
يُمكن تشغيل سكربتك المحلي بشكل صحيح، لكن نظام CI يُظهر "مُفسر غير صالح: /bin/bash^M". هذا هو مشكلة توقف السطر (CRLF). اعرف ما يسببها، وكيف تكتشفها، وكيف تصل إلى حل دائم باستخدام ملف .gitattributes.
يُمكن تشغيل سكربتك في جهازك، لكن عند رفعه إلى 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) | |
|---|---|---|
| البُايتس | 0x0A | 0x0D 0x0A |
| يُستخدم في | لينكس، مك أوس، يونكس | ويندوز، مس دوس |
| مُستقر لسكربتات الشيل | نعم | لا — يُسبب تلفًا في مُحدد المُفسر |
| مُستقر لـ Dockerfile | نعم | لا |
| مُستقر لملفات .env | نعم | لا — يضيف حرفًا خلفيًا إلى القيم |
| مُوصى به من Git | يُعاد تطبيعه إلى LF داخل المخزن | مُخصص فقط لـ .bat/.cmd/.ps1 |
تثبيت ملحقاتنا
أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع
恵 وصلت لوحة النتائج!
لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!
