CRLF vs LF Kesalahan Penghentian Baris yang Menghancurkan CI
Skrip shell Anda berfungsi secara lokal tetapi CI menampilkan 'bad interpreter: /bin/bash^M'. Ini adalah masalah akhir baris CRLF. Pelajari apa yang menyebabkannya, cara mendeteksinya, dan cara memperbaikinya secara permanen dengan .gitattributes.
Skrip shell Anda berjalan sempurna di laptop Anda. Anda mendorongnya ke GitHub. CI gagal dengan pesan yang tidak jelas /bin/bash^M: bad interpreter Anda baru saja terkena bug paling tak terlihat dalam pengembangan perangkat lunak: akhir baris yang salah.
Panduan ini menjelaskan apa sebenarnya CRLF dan LF, mengapa campurannya secara diam-diam merusak skrip dan konfigurasi, serta langkah-langkah tepat untuk mencegah bug akhir baris tidak pernah mencapai pipeline Anda lagi.
Apa Itu CRLF dan LF?
Setiap file teks perlu cara untuk menandai akhir baris. Dua konvensi ada, yang diwarisi dari era mesin teletipe fisik:
- LF (Line Feed) — satu
\nkarakter (byte0x0A). Digunakan oleh Linux, macOS, dan semua sistem berbasis Unix. - CRLF (Carriage Return + Line Feed) — dua karakter,
\r\n(bytes0x0D 0x0A). Digunakan oleh Windows dan MS-DOS.
Nama-nama ini berasal dari mekanisme mesin ketik. Sebuah carriage return menggerakkan kepala cetak kembali ke awal baris. Sebuah line feed memajukan kertas satu baris. Windows mempertahankan keduanya; Unix menghilangkan carriage return yang tidak perlu.
Mengapa CRLF Mengganggu Pipelinen CI?
Di Linux (di mana hampir semua pelaksana CI berjalan), \r bukan spasi — itu adalah karakter literal. Ketika skrip shell disimpan dengan akhir baris CRLF, setiap baris berakhir dengan \r sebelum baris baru. Kernel menginterpretasikan baris awal sebagai #!/bin/bash\r dan mencari biner secara literal bernama bash\r. Biner tersebut tidak ada.
Kesalahan yang dihasilkan terlihat seperti ini:
: bad interpreter: /bin/bash^M: No such file or directory
Itu ^M adalah cara terminal menampilkan karakter carriage return. Ini tidak terlihat di sebagian besar editor teks, yang membuat bug ini sangat membingungkan.
Lokasi-Lokasi Lainnya di Mana CRLF Secara Tak Sadar Menyebabkan Kerusakan
- Dockerfile — Perintah
RUNdengan akhir baris CRLF akan menyisipkan\rke dalam setiap perintah, mengganggu perbandingan string dan jalur file. - Skrip Python —
SyntaxError: unexpected character after line continuation characterketika\diikuti oleh\r\n. - file .env — Nilai variabel lingkungan menangkap akhir
\r, sehinggaAPP_ENV=production\rtidak cocok dengan yang diharapkanproduction. - file CSV dan data — Parser yang membaca baris per baris mungkin mencakup
\rdi field terakhir setiap baris. - SSH authorized_keys — File kunci yang dienkripsi dengan CRLF akan ditolak secara diam-diam oleh layanan SSH.
- Perbedaan Git — Setiap baris tampak berubah, menyembunyikan perubahan nyata dalam kebisingan.
Cara Mendeteksi Masalah Akhir Baris
Sebagian besar editor menyembunyikan akhir baris secara default. Berikut adalah cara yang dapat dipercaya untuk memeriksa:
Menggunakan cat atau hexdump
# Show ^M characters
cat -A yourfile.sh | head -5
# Hex dump to see 0x0d (CR) characters
hexdump -C yourfile.sh | head -10
Menggunakan perintah file
file yourfile.sh
# CRLF output: yourfile.sh: ASCII text, with CRLF line terminators
# LF output: yourfile.sh: ASCII text
Menggunakan grep
# Returns exit code 0 (found) if CRLF endings exist
grep -rlP "\r" . --include="*.sh" --include="*.py" --include="*.yml"
Cara Memperbaiki Akhir Baris CRLF
Pilihan 1: dos2unix (perbaikan cepat satu kali)
dos2unix menghilangkan karakter carriage dari file. Ini tersedia di semua distribusi Linux utama:
# 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
Pilihan 2: sed (tidak perlu alat tambahan)
# Remove carriage returns in-place
sed -i 's/\r//' yourscript.sh
# Or using tr
tr -d '\r' < input.sh > output.sh
Pilihan 3: Perbaikan di VS Code atau JetBrains
Di VS Code, mode akhir baris ditampilkan di status bar (sisi kanan bawah). Klik untuk beralih antara CRLF dan LF untuk file saat ini. Untuk mengubah default untuk file baru, atur "files.eol": "\n" di dalam settings.json.
Di IDE JetBrains, pergi ke File → Line Separators untuk mengubah file saat ini, atau atur default di Editor → Code Style → Line separator.
Solusi yang Tepat: .gitattributes
Perbaikan satu kali tidak dapat diperluas. Solusi yang benar adalah file .gitattributes yang memberi tahu Git secara tepat akhir baris mana yang harus diperkuat, terlepas dari editor atau sistem yang digunakan pengembang.
# .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
Setelah menambahkan file ini, jalankan langkah berikut untuk menormalisasi seluruh repositori dalam satu langkah:
git add --renormalize .
git commit -m "chore: normalize line endings via .gitattributes"
Pengaturan autocrlf Git — dan Mengapa Ini Sering Membuat Hal yang Lebih Buruk
Git memiliki pengaturan core.autocrlf yang berusaha mengonversi akhir baris secara otomatis:
core.autocrlf=true— mengonversi LF ke CRLF saat pengecekan (Windows), CRLF ke LF saat komit. Dimaksudkan untuk pengguna Windows.core.autocrlf=input— mengonversi CRLF ke LF saat komit, tidak melakukan apa-apa saat pengecekan. Lebih aman untuk Mac/Linux.core.autocrlf=false— Git tidak melakukan apa-apa. Yang disimpan oleh editor adalah yang di-commit.
Masalahnya: core.autocrlf adalah pengaturan lokal yang disimpan di ~/.gitconfig. Setiap pengembang di tim Anda memiliki nilai yang berbeda, sehingga komit dari mesin yang berbeda menghasilkan akhir baris yang berbeda. Ini menciptakan kebisingan dalam perbedaan dan kegagalan CI yang tidak konsisten tergantung pada siapa yang terakhir mengubah file.
Aturan umum: gunakan .gitattributes untuk menetapkan kebijakan akhir baris di repositori. Biarkan core.autocrlf menjadi nilai apa pun yang dimiliki setiap pengembang — .gitattributes mengatasi itu.
Menambahkan Perlindungan di CI
Bahkan dengan .gitattributes ditempatkan, penting untuk menambahkan pengecekan eksplisit di CI untuk menangkap file yang terlewat. Langkah dua baris ini mencakup kebanyakan kasus:
# 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
Langkah ini gagal secara jelas di sumber — PR — daripada secara diam-diam saat penerapan.
Referensi Cepat: CRLF vs LF
LF (\n) | CRLF (\r\n) | |
|---|---|---|
| Bytes | 0x0A | 0x0D 0x0A |
| Digunakan oleh | Linux, macOS, Unix | Windows, MS-DOS |
| Aman untuk skrip shell | Ya | Tidak — mengganggu shebang |
| Aman untuk Dockerfile | Ya | TIDAK |
| Aman untuk file .env | Ya | Tidak — menambahkan \r ke nilai |
| Rekomendasi Git | Normalisasi ke LF di repositori | Hanya untuk .bat/.cmd/.ps1 |
Instal Ekstensi Kami
Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat
恵 Papan Skor Telah Tiba!
Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!
Alat Wajib Coba
Lihat semua Pendatang baru
Lihat semuaMemperbarui: Kita alat terbaru ditambahkan pada 28 Apr 2026
