Шаблоны файлов и скриптов в Bash - метод heredoc

В этой статье я расскажу, как можно создавать шаблоны текстовых файлов, например конфигов или скриптов с помощью метода Heredoc

Введение

Heredoc (Here Documents) - это встроенный механизм командных оболочек (bash, sh, zsh), позволяющий передавать команде многострочный текст прямо из терминала, не создавая для этого временный файл. Проще говоря, это способ сказать оболочке: “Всё, что я напишу до слова-метки, воспринимай как единый стандартный ввод (stdin) для программы”. Метка должна быть написана на последней строчке с начала строки.

Базовый синтаксис:

команда << EOF
строка 1
строка 2
строка 3
EOF
  • EOF (End Of File) - это просто популярное имя метки, вместо EOF можно использовать любые другие слова (END, BLOCK, SCRIPT и т.п.).

Пример:

$ cat << EOF
Строка 1
Строка 2
Строка 3
EOF

=== Вывод ===
Строка 1
Строка 2
Строка 3

Подстановка переменных

Подстановка переменных по умолчанию включена:

$ name="Мир"
$ date=$(date)

$ cat << EOF
Привет, ${name}!
Текущая дата: ${date}
EOF

=== Вывод ===
Привет, Мир!
Текущая дата: Ср 12 авг 2026 09:26:00 MSK

Но если взять EOF в кавычки (одинарные или двойные), то переменные не будут подставляться:

$ cat << "EOF"
Привет, ${name}!
Текущая дата: $(date)
EOF

=== Вывод ===

Привет, ${name}!
Текущая дата: $(date)
  • Кавычки вокруг метки нужны, чтобы подстановки не выполняла текущая оболочка до передачи текста.
  • Если метка при открытии была в кавычках, закрывающая метка всё равно пишется без кавычек.

Не забудьте очистить переменные:

$ unset date
$ unset name

Создание файлов

С помощью этого метода можно создавать файлы, например:

$ cat > my_file.txt << EOF
Строка 1
Строка 2
Строка 3
EOF

$ cat my_file.txt
Строка 1
Строка 2
Строка 3

Важно! Если мы хотим этим методом записать файл используя sudo, то должны использовать другую конструкцию.

Так не работает:

sudo cat > /etc/privileged_file << EOF
...
EOF
  • Оболочка обрабатывает перенаправления до того, как запускает команду.
  • Вначале выполняется > /etc/privileged_file без sudo и если нет прав, то мы получаем ошибку.
  • Если на предыдущем шаге мы получили ошибку, то sudo cat вообще не запустится.

Правильный вариант:

sudo tee /etc/privileged_file > /dev/null << EOF
...
EOF
  • Команда tee читает свой стандартный ввод и пишет его в указанный файл, и одновременно в свой стандартный вывод (на экран).
  • Мы запускаем tee через sudo - процесс работает с правами root, и может открыть и записать защищённый файл.
  • Так как команда tee дублирует вывод на экран, то с помощью конструкции > /dev/null мы убираем этот вывод.

Например вот так бы мы создавали файл конфигурации для Nginx:

$ sudo tee /etc/nginx/sites-available/my-site.conf >/dev/null << EOF
server {
    listen 80;
    server_name example.com;
    root /var/www/html;
}
EOF

Использование в скриптах

Метода heredoc можно использовать и при написании своих скриптов на bash, например для оформления красивого вывода:

usage() {
cat << HELP
Использование: $0 [опции]
  -h    Показать эту справку
  -f    Указать файл
HELP
}

Автоматизация

С помощью heredoc можно выполнять многострочные SQL-запросы к базе данных (mysql или psql):

$ mysql -u root -p << EOF
CREATE DATABASE mydb;
USE mydb;
CREATE TABLE users (id INT, name VARCHAR(20));
EOF
  • Пароль будет запрошен отдельно.
  • Для автоматизации лучше настроить доступ без интерактивного ввода пароля: например, через ~/.my.cnf для MySQL/MariaDB или ~/.pgpass для PostgreSQL.

Также мы можем выполнять bash скрипты без создания файлов. В качестве примера приведу мой реальный скрипт первоначальной настройки Debian, в котором я:

  • обновляю систему
  • устанавливаю некоторое ПО
  • устанавливаю локали ru_RU.UTF-8 и en_US.UTF-8
  • делаю локаль ru_RU.UTF-8 по умолчанию
  • устанавливаю московский часовой пояс
bash << "EOF"
set -e
export DEBIAN_FRONTEND=noninteractive

apt-get update && apt-get dist-upgrade -y
apt-get install -y locales wget tzdata lbzip2 bash-completion

sed -i 's/^# *ru_RU.UTF-8/ru_RU.UTF-8/' /etc/locale.gen
grep -q '^ru_RU.UTF-8' /etc/locale.gen || echo 'ru_RU.UTF-8 UTF-8' >> /etc/locale.gen

sed -i 's/^# *en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen
grep -q '^en_US.UTF-8' /etc/locale.gen || echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen

locale-gen
update-locale LANG=ru_RU.UTF-8

ln -fs /usr/share/zoneinfo/Europe/Moscow /etc/localtime
echo Europe/Moscow > /etc/timezone
dpkg-reconfigure -f noninteractive tzdata

echo '. /etc/bash_completion' >> ~/.bashrc
echo 'export LANG="ru_RU.UTF-8"' >> ~/.bashrc

reboot
EOF
  • Обратите внимание, здесь используется "EOF" - экранирование с помощью кавычек.
  • Кавычки вокруг метки подавляют все подстановки (переменные, командные подстановки).
  • Это нужно для того, чтобы в bash команды попадали такими какие они записаны, без кавычек переменные бы раскрылись до попадания в bash.

А также можем автоматизировать интерактивные программы, например sftp:

$ sftp user@server << EOF
put localfile.txt /remote/path/
bye
EOF
  • Для SSH-соединений с паролем такой способ не подойдёт, используйте ключи.

Вопросы для самопроверки

  1. Напишите по памяти базовый синтаксис heredoc с командой cat.
  2. Что обозначает аббревиатура EOF в heredoc? Можно ли использовать другое слово вместо EOF?
  3. Подставляются ли переменные в heredoc? Если да, то можно ли это отключить?
  4. Почему для записи в /etc часто нужно использовать sudo tee, а не sudo cat > file?
  5. Напишите по памяти создание файла с помощью heredoc.
  6. Напишите по памяти создание защищенного файла с помощью heredoc.