In Bash scripting, a "here document" (or "heredoc") redirects multiple lines of input to a command or program, as if you were typing them directly into the terminal. It's a powerful tool for embedding multi-line text within your scripts without needing external files or complex string manipulation.
Delimiter: a heredoc starts with the << operator followed by a delimiter word (often called the "marker" or "terminator").
This delimiter can be any word you choose, but it's common to use something like EOF, END, or TEXT for clarity.
For more readable code, you can use something descriptive as the delimiter, for example END_INSTALLATION_INSTRUCTIONS.
Content: after the initial << DELIMITER, you write the content you want to redirect.
This can be multiple lines of text, code, or anything else.
Termination: the heredoc ends when the delimiter word appears again on a line by itself, with no leading or trailing whitespace.
command << DELIMITER
Content line 1
Content line 2
...
Content line N
DELIMITER
<< DELIMITER and the ending DELIMITER.cat << EOF
This is the first line.
This is the second line.
This is the third line.
EOF
Output:
This is the first line.
This is the second line.
This is the third line.
In this example:
cat is the command.<< EOF starts the heredoc with EOF as the delimiter.EOF on its own line ends the heredoc.cat then outputs the content it received.wc -l << END
Line 1
Line 2
Line 3
END
Output:
3
Here, wc -l counts the number of lines.
The heredoc provides the three lines as input.
The script:
#!/usr/bin/env bash
# Script to process input
while IFS= read -r line; do
echo "Processing: $line"
done
Call the script from an interactive bash prompt with a heredoc:
./your_script << MY_DATA
Item 1
Item 2
Item 3
MY_DATA
Output:
Processing: Item 1
Processing: Item 2
Processing: Item 3
Bash performs variable expansion, command substitution, and arithmetic expansion within a heredoc. In this sense, heredocs act like double quoted strings.
cat << EOF
The value of HOME is $HOME
The current date is $(date)
Two plus two is $((2 + 2))
EOF
Output:
The value of HOME is /home/glennj
The current date is Thu Apr 24 13:47:32 EDT 2025
Two plus two is 4
When the delimiter is quoting (using single or double quotes), these expansions are prevented. The content is taken literally. This is like single quoted strings.
cat << 'EOF'
The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))
EOF
Output:
The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))
If you use <<- (with a trailing hyphen) instead of <<, Bash will strip any leading tab characters from each line of the heredoc.
This is useful for indenting the heredoc content within your script without affecting the output.
# Note, the leading whitespace is tab characters only, not spaces!
# The ending delimiter can have leading tabs as well.
cat <<- END
This line has 1 leading tab.
This line has a leading tab and some spaces.
This line 2 leading tabs.
END
The output is printed with all the leading tabs removed:
This line has 1 leading tab.
This line has a leading tab and some spaces.
This line 2 leading tabs.
The author doesn't recommend this usage. While it can improve the readability of the script,
A typical usage might be to provide some help text:
#!/usr/bin/env bash
usage() {
cat << END_USAGE
Refresh database tables.
usage: ${0##*/} [-h|--help] [-A|--no-archive]
where: --no-archive flag will _skip_ the archive jobs
END_USAGE
}
# ... parsing command line options here ...
if [[ $flag_help == "true" ]]; then
usage
exit 0
fi
Like here documents, here strings (or "herestrings") provide input to a command.
However, while heredocs are given as a block of text, herestrings are given as a single string of text.
Here strings use the <<< "text" syntax.
tr 'a-z' 'A-Z' <<< "upper case this string"
Output:
UPPER CASE THIS STRING
Unlike heredocs, no ending delimiter is required.
A pipeline can be used instead of a here string:
echo "upper case this string" | tr 'a-z' 'A-Z'
So why use a here string?
Consider the case where you get the string as output from a long-running computation, and you want to feed the result to two separate commands. Using pipelines, you have to execute the computation twice:
some_long_running_calculation | first_command
some_long_running_calculation | second_command
A more efficient approach is to capture the output of the computation (using command substutition), and use here strings to provide input to the two subsequent commands:
result=$( some_long_running_calculation )
first_command <<< "$result"
second_command <<< "$result"
Here's a real-world application of that example:
# initialize the output CSV file
echo "ID,VALUE" > data.csv
url='https//example.com/api/query?page=1'
while true; do
json=$( curl "$url" )
# convert the results part of the response into CSV
jq -r '.results[] | [.id, .value] | @csv' <<< "$json"
# get the URL for the next page
url=$( jq -r '.next_url // ""' <<< "$json" )
if [[ "$url" == "" ]]; then
break
fi
done >> data.csv
Note the position of the output redirection.
All output from the while loop will be appended to the file data.csv.
Because these are just forms of redirection, they can be combined with other redirection operations:
cat <<< END_OF_TEXT > output.txt
This is my important text.
END_OF_TEXT
awk '...' <<< "$my_var" >> result.csv
Here documents (or "heredocs") are a flexible and convenient way to manage multi-line input in Bash scripts. They simplify the process of embedding text and data directly within your scripts, making them more self-contained and easier to read.
Here strings (or "herestrings") are like here documents, but offer a simpler, more dynamic syntax.