A here-document — heredoc — embeds a multi-line text block inside a script and feeds it to a command's stdin. It is how scripts write config files, feed SQL to a client, and print usage text without a forest of echo statements.
The basic form #
cat > /etc/aospix/agent.conf <<EOF
host = aospix.com
port = 443
token = $TOKEN
EOF
Everything between <<EOF and the closing EOF goes to cat's stdin — the shell reads until the delimiter appears alone on a line. The redirect works like any other: > creates, >> appends, | pipes onward.
Quoting the delimiter #
By default the body is expanded: variables fill in, commands run. Quote the delimiter and the body becomes literal — the difference between a template and a copy:
$ name=world
$ cat <<EOF
hello $name
EOF
hello world
$ cat <<'EOF'
hello $name
EOF
hello $name
The quoted form is the right one for feeding interpreters — SQL, awk programs, scripts you are generating:
psql "$DB" <<'SQL'
SELECT count(*) FROM subscribers
WHERE created_at > now() - interval '7 days';
SQL
The forms #
| Form | Behaviour |
|---|---|
<<EOF | Expand variables and commands in the body |
<<'EOF' | Fully literal body |
<<-EOF | Strip leading tabs — indented scripts |
<<<word | Here-string: one word as stdin |
<<- only strips tabs, which clashes with space-indented scripts — either indent the body with tabs or accept column zero for the closer. The here-string <<< is the everyday shortcut for "run this command on one line of input":
$ grep -F "$token" <<< "$secrets_file_contents"
$ bc <<< "2^16"
In practice #
Heredocs shine for templated config with a couple of variables — write the file atomically, then move it into place:
tmp=$(mktemp)
cat > "$tmp" <<EOF
server {
listen 443 ssl;
server_name ${domain};
}
EOF
nginx -t -c "$tmp" && sudo mv "$tmp" "/etc/nginx/sites/${domain}"
The expansion rules are the quoting rules with a new syntax; combined with trap cleanup for the mktemp file, this is production-safe. The scripting series continues.
