Что такое heredoc (<<EOF) — и почему из скрипта пропали все $

Ты просишь ИИ создать скрипт деплоя прямо из терминала. Он присылает команду:
cat > deploy.sh <<EOF
echo "Деплою ветку $1 в $HOME/app"
EOF
Ты запускаешь. Файл появился. Открываешь — а внутри:
echo "Деплою ветку в /home/user/app"
$1 пропал. $HOME превратился в твою настоящую папку. Скрипт сломан ещё до первого запуска. Виноват не ИИ и не bash. Виновато одно отсутствующее слово в кавычках. Разберём, что такое heredoc — и где он кусается.
Что такое heredoc
Heredoc (от here document, «документ прямо здесь») — способ передать команде несколько строк текста прямо в терминале. Без отдельного файла.
Выглядит так:
cat <<EOF
Первая строка
Вторая строка
EOF
Знак << говорит: «дальше идёт текст». Слово после него — метка конца. Bash собирает все строки, пока не встретит эту метку одну на строке. Потом отдаёт собранное команде на вход, как будто текст лежал в файле.
Метка не обязана быть EOF. Можно END, TEXT, PROMPT. EOF просто привычка — от end of file, «конец файла».
Почему пропали $
Вот главная хитрость. Без кавычек heredoc ведёт себя как строка в двойных кавычках. Bash заглядывает внутрь и подставляет всё, что похоже на переменную или команду:
$HOME→ твоя домашняя папка;$1→ первый аргумент. В терминале его нет, значит, пусто;$(date)→ текущая дата. Команда выполнится прямо сейчас.
Иногда это то, что надо. Например, собрать письмо с именем пользователя. Но когда ты пишешь скрипт в файл, подстановка всё ломает. $ должны попасть в файл как есть, а сработать потом.
Лечится одной парой кавычек вокруг метки:
cat > deploy.sh <<'EOF'
echo "Деплою ветку $1 в $HOME/app"
EOF
Теперь bash не трогает ничего внутри. В файл ляжет ровно то, что ты видишь. Правило на всю жизнь: пишешь код в файл — бери 'EOF' в кавычках.
А если нужно и то и другое? Скажем, порт подставить сейчас, а $1 оставить на потом. Тогда метку оставь без кавычек, а нужные $ экранируй обратным слешем:
cat > run.sh <<EOF
echo "Запускаю \$1 на порту $PORT"
EOF
Здесь \$1 попадёт в файл как $1. А $PORT возьмётся из переменной окружения прямо сейчас. Смешивать так можно, но читать такое тяжело. Если экранированных $ больше двух — раздели текст на два heredoc.
Где ты его уже видел
Посмотри, как агенты вроде Claude Code делают коммит. Часто так:
git commit -m "$(cat <<'EOF'
fix: handle empty cart
Cart page crashed when the list was empty.
EOF
)"
Выглядит страшно, но теперь ты можешь прочитать это сам. Heredoc собирает многострочное сообщение. $(...) превращает его в одну строку для -m. А кавычки вокруг 'EOF' защищают текст: если в сообщении встретится $PATH или обратная кавычка, они не выполнятся.
Тот же приём годится, чтобы скормить модели длинный промпт из терминала. Например, claude -p с текстом через heredoc. Многострочный запрос без возни с экранированием.
Три грабли, на которые наступают все
1. Метка конца с пробелом. Закрывающее EOF должно стоять одно на строке. Без пробелов после и без отступа перед. Иначе bash его не узнает и будет ждать дальше. В скрипте это кончится предупреждением here-document delimited by end-of-file. В терминале — приглашением >, которое не уходит. Если такое случилось, про выход читай в разборе почему завис терминал.
2. Отступы через <<-. Хочешь красиво сдвинуть heredoc внутри if? Есть вариант <<-EOF: он срезает отступы в начале строк. Но только табы. Редактор тихо заменил табы пробелами — и heredoc перестал закрываться.
3. sudo и права. Команда sudo cat <<EOF > /etc/app.conf падает с Permission denied. Потому что > выполняет твоя оболочка, а не sudo. Права есть у cat, а файл открывает обычный ты. Рабочий вариант:
sudo tee /etc/app.conf > /dev/null <<'EOF'
port=8080
EOF
Здесь файл открывает tee, и он уже запущен через sudo.
Что такое <<< в bash?
Это here-string, младший брат heredoc. Он передаёт на вход одну строку: grep error <<< "$log". Удобно, когда текст уже лежит в переменной.
Чем heredoc лучше echo?
echo хорош для одной строки. На многострочном тексте начинаются \n, экранирование кавычек и разное поведение в разных оболочках. Heredoc читается как обычный текст — что видишь, то и получишь.
Heredoc работает в Windows?
В PowerShell похожая штука называется here-string и пишется через @' ... '@. В WSL и Git Bash работает обычный bash-heredoc.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





