> For the complete documentation index, see [llms.txt](https://freebsdfrau.gitbook.io/serious-shell-programming/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsdfrau.gitbook.io/serious-shell-programming/basics/here-documents/indented-here-doc.md).

# Indented Here Doc

When shell encounters the syntax `<<- delimiter` (or `<<- 'delimiter'`or `<<- "delimiter"`), it starts creating a multi-line buffer using these rules:

1. A line beginning with any amount of `TAB` and then exactly `delimiter` will end the contents
2. All leading occurrences of `TAB` on each line will be removed

The buffer created is sent as `stdin` to the program of your choice. For example:

```
1 #!/bin/sh
2 if : true; then
3     cat <<- EOF
4     This is line 1
5         This is line 2
6     EOF
7 fi
```

Produces:

```
This is line 1
This is line 2
```

The leading tabs are stripped from each line.

This may be undesirable and a simple solution exists.

```
1 #!/bin/sh
2 if : true; then
3     awk 'sub(/^\\\t/,"\t")||1' <<- EOF
4     This is line 1
5     \    This is line 2
6     EOF
7 fi
```

We changed the invocation of `cat` on line 3 to `awk` and a backslash (`\`) was placed before `TAB` series we want to retain.

The `awk` command removes any occurrences of backslash at the start of a line if the following character is `TAB`.

In `awk` the syntax is `PREDICATE { ACTION }`. The `{ ACTION }` part is optional and if not present, defaults to `{ print }` which prints the contents of `$0`, usually the entire line but this can be modified. The `PREDICATE` part is either a series of conditions or a keyword such as `BEGIN` or `END`. If you have an action without predicate, the action is run for every line. When using one or more conditions as a predicate without an action, if the predicate as-a-whole evaluates to non-zero, the line is printed.

Using `sub()` in the predicate part of the `awk` syntax would normally mean the default action of `{ print }` will only execute for the line if the regular expression `/^\\\t/` is found. However, because we have placed `||1` after the invocation of `sub()` in the `awk` predicate, a zero result from `sub()` is translated to one. The result is that every line is printed, regardless of whether `sub()` was able to make a replacement.

You can use the indented here-doc technique with dynamic content too. Take the following text snippet for example, stored in a file named `TABTEST`:

```
Line 1
    Line 2
        Line 3
```

A script can import the contents of this file and embed it in an indented here-doc:

```
1 #!/bin/sh
2 if : true; then
3     awk 'sub(/^\\\t/,"\t")||1' <<- EOF
4     $( cat TABTEST )
5     \            Line 4
6     \                Line 5
7     EOF
8 fi
```

Produces:

```
Line 1
    Line 2
        Line 3
            Line 4
                Line 5
```

The information from `TABTEST` does not need to be modified because the stripping of leading tab characters by using `>>-` happens only on pre-interpolated text. That is to explain that line 4 of the above script, leading tabs are stripped on `$( cat TABTEST )` before it is translated into the contents of `TABTEST`, not after.
