Tidak suka iklan? Pergi Bebas Iklan Hari ini

CRLF vs LF Kesalahan Penghentian Baris yang Menghancurkan CI

Diterbitkan pada

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.

CRLF vs LF: Bug Akhir Baris yang Mengganggu CI 1
IKLAN · HAPUS?

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 \n karakter (byte 0x0A). Digunakan oleh Linux, macOS, dan semua sistem berbasis Unix.
  • CRLF (Carriage Return + Line Feed) — dua karakter, \r\n (bytes 0x0D 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 RUN dengan akhir baris CRLF akan menyisipkan \r ke dalam setiap perintah, mengganggu perbandingan string dan jalur file.
  • Skrip PythonSyntaxError: unexpected character after line continuation character ketika \ diikuti oleh \r\n.
  • file .env — Nilai variabel lingkungan menangkap akhir \r, sehingga APP_ENV=production\r tidak cocok dengan yang diharapkan production.
  • file CSV dan data — Parser yang membaca baris per baris mungkin mencakup \r di 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)
Bytes0x0A0x0D 0x0A
Digunakan olehLinux, macOS, UnixWindows, MS-DOS
Aman untuk skrip shellYaTidak — mengganggu shebang
Aman untuk DockerfileYaTIDAK
Aman untuk file .envYaTidak — menambahkan \r ke nilai
Rekomendasi GitNormalisasi ke LF di repositoriHanya untuk .bat/.cmd/.ps1
Ingin bebas iklan? Bebas Iklan Hari Ini

Instal Ekstensi Kami

Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat

Ke Ekstensi Chrome Ke Ekstensi Tepi Ke Ekstensi Firefox Ke Ekstensi Opera

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!

IKLAN · HAPUS?
IKLAN · HAPUS?
IKLAN · HAPUS?

Pojok Berita dengan Sorotan Teknologi

Terlibat

Bantu kami untuk terus menyediakan alat gratis yang berharga

Belikan aku kopi
IKLAN · HAPUS?