Bash-скрипт не запускается после Windows: CRLF и bad interpreter
Скрипт лежит на месте, bash установлен, право исполнения есть. Однако Linux отвечает bad interpreter или сообщает, что /usr/bin/env не может найти bash с необычным символом в конце. Если файл редактировали в Windows, вероятная причина — окончания строк CRLF.
В текстовом редакторе строка выглядит обычной. На уровне байтов после слова bash идут два символа: возврат каретки и перевод строки. Linux ожидает перевод строки; лишний CR может стать частью имени интерпретатора или аргумента env. Переустановка Bash такой файл не исправляет.
Проверим байты и подготовим исправленную копию
Следующая программа Python сама создаёт учебный сломанный скрипт во временном каталоге, читает его как байты и формирует версию с LF. Она не меняет файлы репозитория. Python удобен для диагностики здесь именно потому, что не скрывает символ CR при текстовом чтении.
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
source = Path(folder) / "broken.sh"
source.write_bytes(b"#!/usr/bin/env bash\r\nprintf 'ready\\n'\r\n")
data = source.read_bytes()
first = data.split(b"\n", 1)[0]
print(repr(first))
fixed = Path(folder) / "fixed.sh"
fixed.write_bytes(data.replace(b"\r\n", b"\n"))
assert b"\r\n" not in fixed.read_bytes()
print(repr(fixed.read_bytes().split(b"\n", 1)[0]))
Первая распечатка заканчивается на bash\r, вторая — на bash. Замена затрагивает последовательности CRLF, а не все символы CR подряд. Это разумная операция для обычного текстового shell-скрипта; двоичные файлы таким способом нормализовать нельзя.
Для своего файла сначала посмотрите первую строку: sed -n '1l' script.sh показывает непечатаемые символы. Затем выберите в редакторе окончания LF и сохраните файл. Полезно сравнить diff: массовое изменение каждой строки часто указывает на нормализацию окончаний, а не переписывание логики.
Почему запуск через bash меняет симптом
При непосредственном ./script.sh интерпретатор выбирается по первой строке файла. Вызов bash script.sh уже запускает выбранный Bash и передаёт ему файл. Поэтому он может обойти ошибку первой строки, но CR в остальных строках продолжат мешать командам: появятся странные аргументы или сообщения command not found. Это диагностическая подсказка, а не полноценное исправление.
Если окончания уже LF, проверяйте первую строку дальше. До #! не должно быть посторонних байтов, путь к интерпретатору должен существовать в целевой среде. Путь из Windows внутри Linux-контейнера не станет Linux-путём автоматически. И отдельно проверьте, что файл действительно доступен для исполнения: ошибка прав возникает раньше чтения полезных команд.
Закрепим договор в репозитории
Строка *.sh text eol=lf в .gitattributes задаёт требуемый формат рабочих shell-файлов. Добавьте её отдельным понятным изменением и проверьте затронутые скрипты с новым checkout. Уже существующие рабочие файлы могут потребовать явного пересохранения; одно добавление атрибута не гарантирует немедленного изменения каждого локального файла.
После исправления выполните bash -n script.sh для синтаксиса, затем безопасный сценарий запуска с учебными входами. Проверка синтаксиса не доказывает правильность бизнес-операций, зато быстро обнаруживает оставшиеся символы и ошибки кавычек. Так проблема перестаёт возвращаться при следующем редактировании на другой системе.
Понимание файлов, интерпретатора и Git особенно полезно при передаче скриптов между Windows и Linux. В программе Bash Linux + Git есть работа с терминалом, написание скриптов и командная история изменений. Откройте курс и закрепите полный путь от файла до повторяемого запуска.
Продолжить на практике
- Bash Linux + Git: от новичка до уверенного пользователя ↗
Освоим два важнейших инструмента разработчика: применение bash-скриптов в практических задачах и git в его самых разных проявлениях.
