---
title: 'Here Documents and Here Strings'
source: 'https://academia.sh/en/courses/shell-programming/here-documents-and-here-strings'
course: 'Shell Programming'
language: en
updated: '2026-08-17T18:10:02+00:00'
license: 'CC BY-SA 4.0'
---

# Here Documents and Here Strings

Input embedded in a script, the effect of quoting the delimiter on expansion, indentation removal, and single-line input notation.

The previous three lessons showed two sources of input: the keyboard and a
file. Scripts need a third source as well — fixed content embedded in the
script's own text. A configuration template, a multi-line usage message, test
data, and a database query all belong to this class.

Keeping this content in a separate file makes the script unable to travel on
its own. Writing every line with a separate `echo` requires managing line
endings, quotes, and spaces by hand. The shell defines two notations for this
job.

## The Here Document

A **here document** begins by writing a **delimiter** after the `<<`
operator; the text up to the line where the delimiter appears alone becomes
the command's standard input.

```sh
code=404
cat <<END
Requested code: $code
END
```

```
Requested code: 404
```

The delimiter's name is free; a string that will not appear in the content is
chosen. `EOF` is a common choice, but it is not mandatory — this course will
use `END`.

Two rules are strict. No character other than the delimiter itself can be on
the delimiter's line; even a single trailing space disqualifies the line from
being the delimiter, and the shell keeps reading to the end of the file and
reports an error. Second, the delimiter's line cannot be indented — an
exception to this is covered below.

## Expansion and Quoting the Delimiter

A here document's content is, by default, processed as if it were inside
double quotes: variable expansion, command substitution, and arithmetic
expansion are applied.

```sh
name="report"
cat <<END
Log: $name.log
Line count: $(grep -c '' access.log)
Arithmetic: 5 * 3 = $((5 * 3))
END
```

```
Log: report.log
Line count: 30
Arithmetic: 5 * 3 = 15
```

If the delimiter is **quoted**, no expansion is done at all; the text passes
through literally. Quoting can be done with single quotes, double quotes, or
a backslash; all three give the same result.

```sh
cat <<'END'
Log: $name.log
Command: $(pwd)
Arithmetic: $((5 * 3))
END
```

```
Log: $name.log
Command: $(pwd)
Arithmetic: $((5 * 3))
```

This distinction is not a stylistic flourish but a condition for correctness.
When embedding another script's source code, a shell configuration, or any
template containing `$` into a script, if the delimiter is not quoted, the
`$` signs in the embedded text get expanded in the outer script and the
content silently breaks. As a rule: **the delimiter is left unquoted only if
expansion is deliberately wanted; otherwise it is quoted.**

## Removing Indentation

A here document does not follow the surrounding code's indentation when it is
inside an `if` or `for` body; because the content is transferred literally,
the indentation enters the text too. The `<<-` form partially solves this
problem: it strips the **leading tab characters** from the content lines and
the delimiter line.

```sh
if true; then
	cat <<-END
		indented line
		second line
	END
fi
```

```
indented line
second line
```

The restriction is plain: only the tab character is stripped, not spaces. In
a file indented with spaces, `<<-` does nothing at all. This is one of the
corners of the shell language that has been kept portable but stayed
impractical; if your style rules require space indentation, leaving the here
document unindented is less surprising.

## The Here String

A **here string** is a shorthand for single-line input. The value to the
right of the `<<<` operator becomes the command's standard input, with a
newline appended to its end.

```sh
name="report"
tr 'a-z' 'A-Z' <<< "$name ready"
```

```
REPORT READY
```

The appended newline is real and it counts:

```sh
wc -c <<< "abc"
printf '%s' "abc" | wc -c
```

```
       4
       3
```

This difference matters in jobs that count length or compare byte for byte.
If you do not want the newline, use a `printf` pipeline instead of a here
string.

The most common use of a here string is splitting a string into its fields:

```sh
read -r address code <<< "10.0.0.12 200"
echo "address=$address code=$code"
```

```
address=10.0.0.12 code=200
```

Doing the same job with a pipeline would fall into the previous lesson's
subshell trap: in the notation `echo ... | read`, `read` runs in a subshell
and the variables it assigns are lost. A here string is redirection, not a
pipe; the command stays in the parent shell. `<<<` is a bash extension,
absent from the POSIX shell language.

## In a Pipeline and With Redirection

A here document is also an ordinary input redirection; it can be used
together with the command's other redirections and with a pipeline.
Redirections are collected at the end of the command line, while the content
starts from the lines following the command:

```sh
sort <<'END' | head -2
gamma
alpha
beta
END
```

```
alpha
beta
```

Writing the output to a target is also specified on the same line. This is
the most direct way to dump fixed content into a file:

```sh
cat > mini.log <<'END'
10.0.0.1 GET /a 200
10.0.0.2 GET /b 404
END
```

```sh
wc -l mini.log
```

```
       2 mini.log
```

## Reproducing the Log

This course's `access.log` file is not downloaded from an external source; it
is produced by a script. This way, every reader works with the same data, and
results become directly comparable.

The `make-data.sh` script below writes the log from scratch. The delimiter is
quoted: even though `$` does not appear in the content, not leaving data text
open to expansion is the correct default.

```sh
cat > make-data.sh <<'SCRIPT_END'
cat > access.log <<'END'
10.0.0.12 - - [07/Feb/2024:09:12:44 +0000] "GET /index.html HTTP/1.1" 200 5120
10.0.0.31 - - [07/Feb/2024:09:12:51 +0000] "GET /static/style.css HTTP/1.1" 200 2048
END
SCRIPT_END
```

The outer delimiter (`SCRIPT_END`) and the inner one (`END`) were chosen
differently. If they were the same, the outer document would end at the
first `END` line, and the rest of the text would be interpreted as commands.
When writing nested here documents, the delimiters must be different.

The fragment above contains the first two of the thirty lines from the start
of the lesson; the full data set was given in the Standard Input, Output, and
Error lesson. Build your own `make-data.sh` file with all thirty lines; in
later lessons, the log will be recoverable with a single command if it gets
corrupted.

## Which One, When

| Notation | Input | Expansion | Typical use |
|---|---|---|---|
| `<< END` | multi-line | applied | template, formatted report header |
| `<< 'END'` | multi-line | not applied | embedded source code, fixed data |
| `<<<` | single-line | applied | splitting a string into fields, filtering one line |

As a fourth option, a `printf` pipeline can always be used and is fully
portable; in exchange, it requires managing newlines by hand.

## Summary

- A here document begins with `<<` and makes the text up to the line where
  the delimiter appears alone the command's standard input.
- If the delimiter is not quoted, the content is expanded by the
  double-quote rule; if quoted, it passes through literally. Quoting is
  mandatory for embedded source code.
- The `<<-` form strips only tab indentation; it has no effect on space
  indentation.
- A here string gives single-line input and appends a newline to its end;
  unlike a pipeline, it does not push the command into a subshell.
- In nested here documents, the delimiters must be chosen differently.

## Next Step

Every example in this topic assumed the commands succeeded. In reality,
`grep` reports a result when it finds no match, `cut` when it cannot open the
file, or `sort` when it finds no disk space. The next lesson defines that
report — the exit status: why zero means success, how the `&&` and `||`
operators tie to this code, and the code ranges the shell reserves for
itself.
