I'm trying to capture a multi-line here-document in a Bash variable using command substitution. The backtick version works, but this version behaves unexpectedly:
MYDOC=$(cat <<LIST
Testing a multi-line
input assignment using $(...)
LIST )
The resulting variable contains the text `LIST`, while the equivalent backtick form places the closing backtick on its own line and works as expected. A standalone `cat <<LIST` also behaves normally.
Why does `$(...)` include the delimiter in the captured text? Is this a command-substitution difference, a Bash parsing rule, or a bug? I'm using Bash 3.2.57 on macOS 15.5.
3 Answers
The here-document terminator must be on a line by itself, with no trailing characters or whitespace. In the first example, the shell sees `LIST )`, not `LIST`, so the here-document is not terminated there. The `)` then closes the command substitution, and Bash reaches the end of the script while still looking for the delimiter, leaving that text in the input.
Put the closing parenthesis on the following line:
MYDOC=$(cat <<LIST
Testing a multi-line
input assignment using $(...)
LIST
)
The backtick version already has its closing backtick on a separate line, which is why it works.
A here-document is terminated only by a line containing the delimiter itself. For an unquoted delimiter, the body is also subject to expansions, so shell syntax appearing inside the body can be interpreted unless it is escaped or the delimiter is quoted.
If the text is meant to be literal, use a quoted delimiter:
MYDOC=$(cat <<'LIST'
Testing a multi-line
input assignment using $(...)
LIST
)
In this form, the `$()` inside the document is preserved literally rather than executed. For simple fixed text, a direct quoted assignment is another good option.
This isn't a difference between backticks and `$(...)`; it's about how the here-document is parsed. The delimiter word must appear exactly as specified, followed immediately by a newline. `LIST )` cannot terminate a document whose delimiter is `LIST`.
`$(...)` is still preferable because it can be nested cleanly, unlike backticks, but its closing `)` must be placed after the here-document terminator. Also, quote variables when expanding multi-line values—for example, use `printf '%sn' "$MYDOC"`—so whitespace and newlines aren't subject to word splitting.
Some newer Bash versions may accept a closing `)` directly after the delimiter with a warning, but that is not portable behavior. Keeping the delimiter alone on its own line is the reliable form, especially with older Bash versions such as the one shipped with macOS.

That was the confusing part for me—I assumed the delimiter could share a line with the closing command-substitution syntax. Keeping the terminator completely alone fixes it.