CRLF против LF ОШИБКА ЗАКРЫТИЯ СТРОКИ, КОТОРАЯ ПРОБЛЕМАМИ В ПРОЦЕССЕ ИНТЕГРАЦИИ

Опубликовано

Ваш скрипт работает локально, но в CI возникает ошибка "плохой интерпретатор: /bin/bash^M". Это ошибка завершения строк CRLF. Изучите, что вызывает эту ошибку, как её обнаружить и как навсегда исправить с помощью .gitattributes.

CRLF против LF: Ошибка завершения строки, которая нарушает CI 1
Реклама · УДАЛИТЬ?

Ваш скрипт shell работает идеально на ноутбуке. Вы отправляете его в GitHub. CI завершается с неясной ошибкой. /bin/bash^M: bad interpreter Вы только что пострадали от самого невидимого бага в разработке программного обеспечения: неправильного завершения строки.

Этот гайд объясняет, что такое CRLF и LF, почему смешивание их безвозвратно повреждает скрипты и конфигурации, и какие конкретные шаги нужно предпринять, чтобы баги с завершением строки больше не попадали в вашу цепочку развертывания.

Что такое CRLF и LF?

Каждый текстовый файл должен иметь способ отметить конец строки. Существуют две конвенции, наследуемые от эпохи физических телетайпов:

  • LF (Line Feed) — один \n символ (байт 0x0A). Используется в Linux, macOS и всех системах, производимых на основе Unix.
  • CRLF (Carriage Return + Line Feed) — два символа, \r\n (байты 0x0D 0x0A). Используется в Windows и MS-DOS.

Названия происходят из механики старых печатающих машин. Символ carriage return возвращал печатающий шарнир в начало строки. Символ line feed двигал бумагу на одну строку вперёд. Windows сохраняла оба символа; Unix убрала избыточный символ carriage return.

Почему CRLF нарушает CI-трубопроводы?

На Linux (где почти все запускаемые CI-режимы выполняются), символ \r не является пробелом — это буквальный символ. Когда скрипт shell сохраняется с завершением строки CRLF, каждая строка заканчивается \r до символа новой строки. Ядро интерпретирует строку с указанием программы как #!/bin/bash\r и ищет исполняемый файл, буквально названный bash\r. Такого исполняемого файла не существует.

Появляется следующая ошибка:

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

The ^M — это то, как терминал отображает символ carriage return. Он невидим в большинстве текстовых редакторов, что делает этот баг особенно ошеломляющим.

Другие места, где CRLF безвозвратно наносит вред

  • Dockerfiles — инструкция с завершением строки CRLF вставит RUN в каждую команду, нарушая сравнения строк и пути к файлам. \r Python скрипты
  • приSyntaxError: unexpected character after line continuation character следующем за \ .env файлы \r\n.
  • — значения переменных среды получают завершающий символ , поэтому \rникогда не совпадает с ожидаемым APP_ENV=production\r CSV и данные файлы production.
  • — парсеры, читающие строки построчно, могут включать в последнее поле каждой строки. \r SSH authorized_keys
  • — файл с ключами, закодированными в CRLF, будет безусловно отклонён SSH-демоном. Git diffs
  • — каждая строка кажется изменённой, скрывая реальные изменения в шуме. Как обнаружить проблемы с завершением строки?

Большинство редакторов скрывают завершения строк по умолчанию. Ниже приведены надёжные способы проверки:

Использование cat или hexdump

Использование команды file

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

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

Использование grep

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

Как исправить завершения строк CRLF?

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

Вариант 1: dos2unix (самый быстрый одноразовый способ)

удаляет символы возврата из файлов. Он доступен на всех основных дистрибутивах Linux:

dos2unix Вариант 2: sed (без дополнительных инструментов)

# 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

Вариант 3: исправление в VS Code или JetBrains

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

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

В VS Code режим завершения строки отображается в статус-баре (нижний правый угол). Нажмите на него, чтобы переключиться между CRLF и LF для текущего файла. Чтобы изменить значение по умолчанию для новых файлов, установите

в вашем "files.eol": "\n" В IDE-системах JetBrains перейдите к settings.json.

Файл → Разделители строк для изменения текущего файла или установите значение по умолчанию в Редактор → Стиль кода → Разделитель строк Правильное решение: файл .gitattributes.

Одноразовые исправления не масштабируются. Правильное решение — это файл

который устанавливает точные правила по завершению строк, независимо от того, какой редактор или ОС используют разработчики. .gitattributes После добавления этого файла выполните следующее, чтобы нормализовать весь репозиторий за один раз:

# .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 autocrlf — и почему она часто усугубляет проблему

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

Git имеет настройку

которая пытается автоматически преобразовывать завершения строк: core.autocrlf — преобразует LF в CRLF при проверке (на Windows), CRLF в LF при коммите. Предназначена для пользователей Windows.

  • core.autocrlf=true — преобразует CRLF в LF при коммите, не делает ничего при проверке. Более безопасно для Mac/Linux.
  • core.autocrlf=input — Git ничего не делает. То, что сохраняет редактор, то и коммитится.
  • core.autocrlf=false Проблема:

— это локальная настройка, сохраняемая в core.autocrlf . Каждый разработчик в команде имеет разное значение, поэтому коммиты с разных машин генерируют разные завершения строк. Это создаёт постоянный шум в сравнениях и непредсказуемые сбои CI в зависимости от того, кто последний изменил файл. Правило на память: используйте ~/.gitconfigдля установки политики завершения строк в репозитории. Пусть

будет тем, что у каждого разработчика — переопределяет это. .gitattributes Добавление проверки в CI core.autocrlf Даже при наличии .gitattributes вместе с ним стоит добавить явную проверку в CI, чтобы обнаружить любые файлы, которые могут просочиться. Двухстрочная команда покрывает большинство случаев:

Этот шаг завершается ярко на уровне PR, а не тихо на уровне развертывания.

Быстрый справочник: CRLF против LF .gitattributes LF (

# 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 (

Байты

Используется в\n)Linux, macOS, Unix\r\n)
Windows, MS-DOS0x0A0x0D 0x0A
Безопасно для скриптов shellНет — нарушает синтаксис shebangБезопасно для Dockerfile
Безопасно для .env файловДаНет — добавляет завершающий \r к значениям
Рекомендация GitДаНет
Нормализовать в LF в репозиторииДаТолько для .bat/.cmd/.ps1
CRLF против LF: Баг с завершением строки, который нарушает CI 2CRLF против LF: Баг с завершением строки, который нарушает CI 1Только для .bat/.cmd/.ps1
Хотите убрать рекламу? Откажитесь от рекламы сегодня

Установите наши расширения

Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска

в Расширение Chrome в Расширение края в Расширение Firefox в Расширение Opera

Табло результатов прибыло!

Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!

Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?

новости с техническими моментами

Примите участие

Помогите нам продолжать предоставлять ценные бесплатные инструменты

Купи мне кофе
Реклама · УДАЛИТЬ?