Reading — step 1 of 5
Read
~2 min readTokenizing & Parsing
Here-Documents (<<EOF)
A here-document lets you embed a multi-line literal as a command's
stdin, right inside your shell script. It's the cleanest way to pass a
fixed payload (a SQL query, a config, an email body) to a tool like
cat, psql, or curl --data-binary @-.
Three flavors
cat <<END # 1. variables expand inside the body
hello $USER
END
cat <<'END' # 2. quoted terminator -> NO expansion (literal)
hello $USER
END
cat <<-END # 3. -EOF strips LEADING TABS from each body line
hello
world
END
Bash treats << differently from <: the body up to the closing
terminator is collected by the parser, then written into a pipe (or
temp file) whose read end becomes the child's stdin.
How the shell implements it
- Parse phase — when the parser sees
<<WORD, it remembersWORDand starts buffering subsequent lines as the heredoc body. - Terminator — collection ends when a line consists exactly of
WORD(no leading/trailing whitespace, unless<<-is used, in which case leading tabs are stripped from the terminator line too). - Expansion — if
WORDwas quoted ('EOF'or"EOF"or\EOF), the body is preserved literally. Otherwise$VARand$(cmd)are expanded — but\nand other backslash escapes stay literal, since a heredoc body does not process them. - Execution phase — bash
pipe(2)s, writes the body into the write end, anddup2(read_fd, 0)in the child beforeexec.
Why you care
- Embedded scripts (
ssh host <<'EOF' ... EOF) are how every Ansible / Capistrano-era deploy script gets work done remotely. - The quoted-vs-unquoted distinction is the #1 source of subtle bugs:
cat <<EOF\n$PATH\nEOFprints your PATH, not the literal string$PATH. <<-EOFexists exactly so you can indent the body to match surrounding code without those indent tabs ending up in the output.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…