CRLF против LF ОШИБКА ЗАКРЫТИЯ СТРОКИ, КОТОРАЯ ПРОБЛЕМАМИ В ПРОЦЕССЕ ИНТЕГРАЦИИ
Ваш скрипт работает локально, но в CI возникает ошибка "плохой интерпретатор: /bin/bash^M". Это ошибка завершения строк CRLF. Изучите, что вызывает эту ошибку, как её обнаружить и как навсегда исправить с помощью .gitattributes.
Ваш скрипт 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в каждую команду, нарушая сравнения строк и пути к файлам.\rPython скрипты - при —
SyntaxError: unexpected character after line continuation characterследующем за\.env файлы\r\n. - — значения переменных среды получают завершающий символ , поэтому
\rникогда не совпадает с ожидаемымAPP_ENV=production\rCSV и данные файлыproduction. - — парсеры, читающие строки построчно, могут включать в последнее поле каждой строки.
\rSSH 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-DOS | 0x0A | 0x0D 0x0A |
| Безопасно для скриптов shell | Нет — нарушает синтаксис shebang | Безопасно для Dockerfile |
| Безопасно для .env файлов | Да | Нет — добавляет завершающий \r к значениям |
| Рекомендация Git | Да | Нет |
| Нормализовать в LF в репозитории | Да | Только для .bat/.cmd/.ps1 |
| CRLF против LF: Баг с завершением строки, который нарушает CI 2 | CRLF против LF: Баг с завершением строки, который нарушает CI 1 | Только для .bat/.cmd/.ps1 |
Установите наши расширения
Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска
恵 Табло результатов прибыло!
Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!
Подписаться на новости
все Новые поступления
всеОбновлять: Наш последний инструмент было добавлено 18 мая 2026
