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

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

Ваш скрипт работает локально, но не работает в CI с ошибкой интерпретатора, которая неясна. Причина обычно заключается в концах строк CRLF. Изучите, что такое CRLF, почему они нарушают работу линейных потоков в Linux, и как решить эту проблему навсегда с помощью .gitattributes и настроек редактора.

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

Ваш скрипт Shell работал корректно на ноутбуке. Вы отправляете его в GitHub, CI его обнаруживает, и пайплайн завершается с криптичной ошибкой — что-то вроде /bin/bash^M: bad interpreter или задача, которая завершается кодом 126 без очевидной причины. Скрипт синтаксически корректен. Вы не изменили ни одной строки. Но сборка сломана.

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

Что означают CRLF и LF

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

  • LF (Line Feed, \n, шестнадцатеричный код 0x0A) — стандарт Unix и Linux. macOS использует его с OS X. Один байт на конец строки.
  • CRLF (Carriage Return + Line Feed, \r\n, шестнадцатеричный код 0x0D 0x0A) — стандарт Windows, наследованный от DOS, который, в свою очередь, наследовал его от телетайпов. Два байта на конец строки.

Названия происходят из физических действий старого типографа: возврат каретки перемещал печатающую головку в начало строки; возврат листа поднимал бумагу на одну строку. DOS кодировал оба действия буквально. Unix выбрал минимальный подход и сохранил только возврат строки.

Почему CI ломается, а на ноутбуке всё работает

Если вы пишете код на Windows и ваш редактор сохраняет файлы с CRLF, всё работает локально — инструменты Windows обрабатывают оба формата прозрачно. Но ваш пайплайн CI, скорее всего, работает на Linux, и интерпретаторы оболочек Linux рассматривают \r как печатаемый символ, а не как пробел. Когда bash читает строку скрипта, заканчивающуюся на \r\n, он видит команду вместе с завершающим возвратом каретки. Результат выглядит так:

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

The ^M в этой ошибке — это то, как терминал отображает \r. Bash пытается выполнить строку с указанием интерпретатора, заканчивающуюся возвратом каретки, и, конечно, интерпретатора по этому пути не существует.

То же самое происходит в более тонких формах:

  • Значения переменных среды с завершающими \r символами безвозвратно нарушают сравнения строк.
  • Скрипты Docker ENTRYPOINT не могут запуститься из-за повреждённого указания на интерпретатор.
  • Скрипты Python читают конфигурационные файлы и получают ключи вроде "setting\r" вместо "setting".
  • Makefiles игнорируют правила, потому что символ таба следует за \r.

Как обнаружить проблему

Перед тем как что-либо исправлять, подтвердите, что файл действительно имеет концы строки CRLF. Несколько быстрых способов:

cat -A (Linux/macOS)

cat -A deploy.sh

Строки, заканчивающиеся на CRLF, будут отображаться как ^M$ в конце. Строки, заканчивающиеся на LF, будут отображаться только как $.

file command

file deploy.sh
# deploy.sh: Bash script, ASCII text, with CRLF line terminators

xxd или hexdump

xxd deploy.sh | grep "0d 0a"

Любые совпадения подтверждают наличие CRLF. Если вы видите только 0a в концах строк, вы в порядке.

Как исправить проблему

dos2unix (самый быстрый способ исправления)

Установите один раз, запустите на любом файле:

# Install
sudo apt install dos2unix    # Debian/Ubuntu
brew install dos2unix        # macOS

# Convert a single file
dos2unix deploy.sh

# Convert all shell scripts in the repo
find . -name "*.sh" -exec dos2unix {} \;

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

sed -i "s/\r//" deploy.sh

tr (соответствует POSIX)

tr -d "\r" < deploy.sh > deploy_fixed.sh && mv deploy_fixed.sh deploy.sh

Как предотвратить проблему: настройка Git

Исправление файлов после того, как они уже были созданы, является реактивным решением. Постоянное решение — указать Git, как обрабатывать концы строк, чтобы проблема никогда не достигала репозитория.

Подход с использованием .gitattributes (рекомендуется)

А .gitattributes Файл, коммитируемый в репозиторий, обеспечивает единообразие концов строк для всех пользователей, независимо от их настроек локального Git. Добавьте это в корень вашего репозитория:

# Normalize all text files to LF in the repo
* text=auto eol=lf

# Explicitly enforce LF for files that must never have CRLF
*.sh text eol=lf
*.bash text eol=lf
Makefile text eol=lf
Dockerfile text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.json text eol=lf

# Binary files — do not touch
*.png binary
*.jpg binary
*.gif binary
*.zip binary
*.gz binary

The text=auto eol=lf параметр указывает Git автоматически определять текстовые файлы и хранить их с LF независимо от ОС разработчика. Явные строки ниже этого параметра фиксируют файлы, где CRLF может вызвать сбои во время выполнения.

После добавления или изменения .gitattributes, переформатируйте существующие файлы в репозитории:

git add --renormalize .
git commit -m "normalize line endings"

Настройка core.autocrlf (только локально)

Git имеет глобальную настройку, называемую core.autocrlf , которая управляет преобразованием концов строк при выдаче и коммите. Рекомендуемые значения зависят от вашей ОС:

# Windows — convert CRLF to LF on commit, LF to CRLF on checkout
git config --global core.autocrlf true

# Linux/macOS — convert CRLF to LF on commit, no conversion on checkout
git config --global core.autocrlf input

Проблема с использованием core.autocrlf один раз: она применяется только на машине, где она настроена. Разработчик, который никогда не настраивал её, может всё ещё отправлять файлы с CRLF. Файл защищён на уровне репозитория, поэтому он строго надёжнее. .gitattributes Настройки редактора, которые нужно правильно задать

Ваш редактор является первым барьером защиты. Несколько простых настроек:

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

VS Code

Редакторы JetBrains (IntelliJ, WebStorm, PyCharm) settings.json:

{
  "files.eol": "\n"
}

Перейдите в

Настройки → Редактор → Стиль кода и установите Разделитель строк Unix и macOS (\n) к . Вы также можете добавить файлв корень репозитория: .editorconfig Большинство современных редакторов уважают

[*]
end_of_line = lf
insert_final_newline = true

по умолчанию или через плагин. Коммитирование его в репозиторий означает, что участники получают единые настройки без необходимости в ручной настройке. .editorconfig Полный чек-лист

Если вы стандартизируете репозиторий, в котором ранее возникали проблемы с концами строк, пройдите по этому списку в порядке:

файл с

  1. Добавьте .gitattributes и явными правилами для скриптов shell, Dockerfiles и конфигурационных файлов. * text=auto eol=lf Добавьте файл
  2. чтобы исправить существующие отслеживаемые файлы. .editorconfig и явными правилами для скриптов shell, Dockerfiles и конфигурационных файлов. end_of_line = lf.
  3. Выполните git add --renormalize . && git commit -m "normalize line endings" на любые неотслеживаемые скрипты, которые будут выполняться на Linux.
  4. Выполните dos2unix глобально на машинах Linux/macOS;
  5. Множество core.autocrlf input на Windows. true Добавьте шаг проверки в CI — что-то вроде
  6. — чтобы обнаруживать любые отклонения до того, как они достигнут деплоя. grep -rUl $'\r' *.sh — чтобы поймать любые регрессии до их достижения в развертывании.
Хотите убрать рекламу? Откажитесь от рекламы сегодня

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

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

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

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

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

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

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

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

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

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