LFCS 29 ๐ง Here Documents and Here Strings
A command reads from the standard input, and the input usually comes from the file or the pipe. But sometimes the input is the short, the inline, and the not worth the file. The cat << EOF feeds the block of the text to the cat without the temporary file. The grep 'pattern' <<< "$variable" feeds the string to the grep without the echo. The here document and the here string are the redirection operators that embed the input directly in the command. They are the shell features, and they are the standard for the configuration generation, the SQL scripts, the SSH commands, and the multiline strings. The LFCS exam tests them as part of the “Use streams, pipes, and redirects” objective.
The here document is the << operator. The syntax is command << DELIMITER followed by the lines of the input, and the DELIMITER line ends the input. The delimiter is the arbitrary word, and the EOF is the convention. The quoted delimiter (<< 'EOF') prevents the variable expansion, and the unquoted (<< EOF) allows it. The <<- strips the leading tabs, and the <<< is the here string for the single line.
The here string is the <<< operator. The syntax is command <<< "string", and the string is the input. The shell appends the newline, and the command reads it as the single line. The here string is the shorthand for the echo "string" | command, and it is the cleaner and the faster.
This chapter covers three areas. First, why the here documents and the here strings exist โ the inline-input problem and the temporary-file avoidance. Second, the syntax โ the <<, the <<-, the <<<, the quoted vs the unquoted delimiter, and the variable expansion. Third, the common patterns โ the SQL scripts, the SSH commands, the configuration generation, the multiline strings, and the pitfalls. The chapter ends with a complete example session, a quick reference, best practices, common pitfalls, real-world examples, and diagrams showing the here document flow.
Key point: The here document is the << operator that feeds the multiline input to the command, and the here string is the <<< operator that feeds the single line. The quoted delimiter (<< 'EOF') prevents the variable expansion, and the unquoted (<< EOF) allows it. The <<- strips the leading tabs. The here document and the here string are the inline input without the temporary file.
Why here documents and here strings exist
The inline-input problem. The command reads the standard input, and the input is the short. The cat file.txt | command needs the file, and the echo 'input' | command needs the pipe. The here document and the here string embed the input directly in the command, and the temporary file and the echo pipe are the unnecessary. The command <<< 'input' is the shorthand for the echo 'input' | command, and the command << EOF is the multiline.
The temporary-file problem. The SQL script, the configuration, and the SSH command need the multiline input. The temporary file is the way, but the file must be created, the written, the read, and the deleted. The here document skips the file: the input is the inline, and the command reads it. The mysql << EOF is the SQL without the schema.sql.
The variable-expansion problem. The here document with the unquoted delimiter allows the variable expansion, and the quoted delimiter prevents it. The cat << EOF expands the $variable, and the cat << 'EOF' does not. The choice is the important for the scripts that generate the configuration with the variables and the scripts that contain the literal $.
The indentation problem. The here document must have the delimiter at the start of the line, unless the <<- is used. The <<- strips the leading tabs, and it allows the indentation in the script. The spaces are not the stripped, only the tabs.
The single-line problem. The here string is the single-line input. The grep 'pattern' <<< "$var" is the cleaner than the echo "$var" | grep 'pattern'. The here string appends the newline, and the command reads it.
The trade-off. The here document is the multiline and the readable, and the here string is the single-line and the concise. The temporary file is the reusable and the debuggable, and the here document is the inline. The trade-off is between the file and the inline, and the choice depends on the reuse.
a. The here document syntax
The here document is the << operator. The syntax is the command << DELIMITER, the lines, and the DELIMITER.
cat << EOF
line 1
line 2
line 3
EOF
The cat reads the three lines, and the EOF ends the input. The delimiter is the arbitrary, and the EOF is the convention.
The variable expansion. The unquoted delimiter expands the variables:
NAME="Alice"
cat << EOF
Hello, $NAME
EOF
# the Hello, Alice
The $NAME is the expanded, and the $(command) is the executed.
The quoted delimiter. The quoted delimiter prevents the expansion:
NAME="Alice"
cat << 'EOF'
Hello, $NAME
EOF
# the Hello, $NAME
The $NAME is the literal, and the $(command) is the literal. The quoted delimiter is the way to pass the literal $.
The <<-. The <<- strips the leading tabs:
if true; then
cat <<- EOF
line 1
line 2
EOF
fi
The leading tabs are the stripped, and the delimiter can be the indented. The spaces are not the stripped, and the tabs are the requirement.
The multiple here documents. The command can have the multiple:
cat << EOF1 << EOF2
the first
EOF1
the second
EOF2
# the cat reads the second only
The cat reads the last one, and the first is the ignored. The multiple is the rare.
The here document in the pipeline. The here document is the stdin, and the pipeline is the stdout:
cat << EOF | grep 'line'
line 1
line 2
EOF
The cat feeds the grep, and the here document is the source.
The here document to the file. The here document with the >:
cat << EOF > config.txt
key=value
EOF
The cat writes the file, and the here document is the content. The cat is the unnecessary, and the > can be used directly:
cat > config.txt << EOF
key=value
EOF
The cat > file << EOF is the pattern, and the cat is the writer.
b. The here string syntax
The here string is the <<< operator. The syntax is the command <<< "string".
grep 'pattern' <<< "the string to search"
wc -w <<< "the one two three"
cat <<< "the single line"
The grep reads the string, and the wc counts the words. The here string is the shorthand for the echo "string" | command.
The variable. The here string with the variable:
NAME="Alice"
grep 'Alice' <<< "$NAME"
The $NAME is the expanded, and the grep reads the Alice.
The command substitution. The here string with the $():
wc -l <<< "$(ls)"
The $(ls) is the executed, and the wc counts the lines.
The arithmetic. The here string with the $(( )):
echo $(( $(cat <<< "5") + 3 ))
# the 8
The here string is the input, and the arithmetic is the output.
The newline. The here string appends the newline:
wc -c <<< "abc"
# the 4 (the a, the b, the c, and the newline)
The here string adds the newline, and the wc -c counts it.
The read with the here string. The read reads the here string:
read -r var <<< "the value"
echo "$var"
# the value
The read reads the var from the here string.
The multiple words. The here string with the multiple words:
read -r first second <<< "the one two three"
echo "$first" # the the
echo "$second" # the one two three
The read splits the words, and the last variable gets the rest.
c. The common patterns
The SQL script. The here document to the mysql:
mysql -u root -p << EOF
SELECT * FROM users;
SELECT * FROM orders;
EOF
The SQL is the inline, and the temporary file is the avoided.
The SSH command. The here document to the ssh:
ssh user@host << EOF
cd /app
git pull
npm install
npm run build
EOF
The commands are the inline, and the ssh runs them.
The configuration generation. The here document with the variables:
cat > /etc/app/config.conf << EOF
[server]
host = $HOST
port = $PORT
[debug]
enabled = $DEBUG
EOF
The variables are the expanded, and the config is the generated.
The literal configuration. The here document with the quoted delimiter:
cat > script.sh << 'EOF'
#!/bin/bash
echo "$HOME"
echo "$(date)"
EOF
The $HOME and the $(date) are the literal, and the script is the generated.
The multiline string. The here string with the $'...':
cat <<< $'line 1\nline 2'
The $'...' interprets the escape, and the \n is the newline.
The JSON generation. The here document with the JSON:
cat > data.json << EOF
{
"name": "$NAME",
"age": $AGE
}
EOF
The variables are the expanded, and the JSON is the generated. The jq is the validation.
The YAML generation. The here document with the YAML:
cat > docker-compose.yml << EOF
services:
app:
image: $IMAGE
ports:
- "$PORT:3000"
EOF
The YAML is the generated, and the variables are the expanded.
The script generation. The here document with the sudo:
sudo tee /etc/systemd/system/app.service << EOF
[Unit]
Description=The App
[Service]
ExecStart=/usr/bin/app
[Install]
WantedBy=multi-user.target
EOF
The sudo tee writes the root-owned file, and the here document is the content.
The nested here document. The here document in the here document:
cat << OUTER
The outer
$(cat << INNER
The inner
INNER
)
The outer again
OUTER
The $() runs the inner, and the outer has the result.
The here document with the while read. The here document with the loop:
while IFS= read -r line; do
echo "Line: $line"
done << EOF
line 1
line 2
EOF
The while read reads the each line, and the here document is the source.
The here string with the jq. The here string with the JSON:
jq '.name' <<< '{"name": "Alice"}'
# the "Alice"
The jq reads the JSON from the here string.
The here document with the openssl. The here document with the openssl:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes << EOF
US
California
San Francisco
The Company
The Department
the.example.com
admin@example.com
EOF
The openssl prompts are the answered, and the certificate is the generated.
Complete Example Session
# ============================================
# PART 1: THE BASIC HERE DOCUMENT
# ============================================
cat << EOF
line 1
line 2
line 3
EOF
# ============================================
# PART 2: THE VARIABLE EXPANSION
# ============================================
NAME="Alice"
cat << EOF
Hello, $NAME
EOF
# the Hello, Alice
# ============================================
# PART 3: THE QUOTED DELIMITER
# ============================================
NAME="Alice"
cat << 'EOF'
Hello, $NAME
EOF
# the Hello, $NAME
# ============================================
# PART 4: THE TABS STRIPPED
# ============================================
if true; then
cat <<- EOF
line 1
line 2
EOF
fi
# ============================================
# PART 5: THE HERE DOCUMENT TO THE FILE
# ============================================
cat > config.txt << EOF
key=value
another=value
EOF
# ============================================
# PART 6: THE SUDO TEE
# ============================================
sudo tee /etc/systemd/system/app.service << EOF
[Unit]
Description=The App
[Service]
ExecStart=/usr/bin/app
[Install]
WantedBy=multi-user.target
EOF
# ============================================
# PART 7: THE HERE STRING
# ============================================
grep 'pattern' <<< "the string to search"
wc -w <<< "the one two three"
read -r var <<< "the value"
echo "$var"
# ============================================
# PART 8: THE HERE STRING WITH THE COMMAND
# ============================================
wc -l <<< "$(ls)"
jq '.name' <<< '{"name": "Alice"}'
echo $(( $(cat <<< "5") + 3 ))
# ============================================
# PART 9: THE SQL SCRIPT
# ============================================
mysql -u root -p << EOF
SELECT * FROM users;
SELECT * FROM orders;
EOF
# ============================================
# PART 10: THE LFCS PIPELINE
# ============================================
# The SSH command
ssh user@host << EOF
cd /app
git pull
npm install
npm run build
EOF
# The config generation
cat > /etc/app/config.conf << EOF
[server]
host = $HOST
port = $PORT
EOF
# The literal script
cat > script.sh << 'SCRIPT'
#!/bin/bash
echo "$HOME"
SCRIPT
chmod +x script.sh
# The while read with the here document
while IFS= read -r line; do
echo "Line: $line"
done << EOF
line 1
line 2
EOF
# The JSON with the here document
cat > data.json << EOF
{
"name": "$NAME",
"age": $AGE
}
EOF
# The here string with the jq
jq '.name' <<< '{"name": "Alice"}'
# The here string with the read
read -r first second <<< "the one two three"
echo "$first" # the the
echo "$second" # the one two three
# The here document with the pipeline
cat << EOF | grep 'line'
line 1
line 2
line 3
EOF
The ten parts show the basic here document, the variable expansion, the quoted delimiter, the tabs stripped, the here document to the file, the sudo tee, the here string, the here string with the command, the SQL script, and the LFCS pipeline.
Quick Reference
The Operators
| The operator | The purpose |
|---|---|
<< DELIM | The here document |
<< 'DELIM' | The here document, no expansion |
<<- DELIM | The here document, the tabs stripped |
<<< "string" | The here string |
The Delimiter
| The form | The expansion |
|---|---|
<< EOF | The variables and the commands |
<< 'EOF' | The literal |
<< "EOF" | The literal (the same as the quoted) |
<< \EOF | The literal (the escaped) |
The Common Patterns
| The pattern | The command |
|---|---|
| The SQL | mysql << EOF |
| The SSH | ssh host << EOF |
| The config | cat > file << EOF |
| The privileged | sudo tee file << EOF |
| The literal | cat > file << 'EOF' |
| The loop | while read << EOF |
| The here string | grep 'x' <<< "$var" |
The Here String vs the Echo Pipe
| The aspect | The here string | The echo pipe |
|---|---|---|
| The syntax | cmd <<< "$var" | echo "$var" | cmd |
| The newline | The appended | The appended |
| The subshell | No | Yes |
| The speed | The faster | The slower |
The Common Commands
| The command | The purpose |
|---|---|
cat << EOF | The read the multiline |
cat > file << EOF | The write the file |
mysql << EOF | The SQL |
ssh host << EOF | The remote |
sudo tee file << EOF | The privileged |
jq <<< '{}' | The JSON |
read <<< "$var" | The parse |
Best Practices
โ Do This:
# Use the quoted delimiter for the literal
cat << 'EOF' # โ
echo "$HOME"
EOF
# Use the here document for the multiline
mysql << EOF # โ
SELECT * FROM users;
EOF
# Use the here string for the single line
grep 'pattern' <<< "$var" # โ
# Use the sudo tee for the privileged
sudo tee /etc/file << EOF # โ
# Use the <<- for the indentation
cat <<- EOF # โ
line
EOF
# Use the read with the here string
read -r var <<< "value" # โ
โ Don’t Do This:
# Don't use the unquoted delimiter for the literal
cat << EOF // โ
echo "$HOME" # the $HOME is the expanded
EOF
# Don't forget the delimiter
cat << EOF // โ
line 1
# the delimiter is the missing
# Don't use the here document for the single line
cat << EOF // โ
(the here string is the simpler)
line
EOF
# Don't use the spaces with the <<-
cat <<- EOF // โ
line # the spaces are NOT stripped, only the tabs
EOF
# Don't forget the newline
printf 'abc' <<< "" // โ
# Don't use the > for the sudo
sudo cat > /etc/file << EOF // โ (the tee is the correct)
EOF
# Don't nest the unquoted here documents
cat << EOF // โ (the $() is the fix)
$(cat << EOF
EOF
)
EOF
Common Pitfalls
| The pitfall | The why | The fix |
|---|---|---|
| The variables expanded | The unquoted delimiter | Use the << 'EOF' |
The $ interpreted | The unquoted | Quote the delimiter |
| The delimiter not found | The wrong word | Match the delimiter |
| The spaces not stripped | The <<- strips the tabs only | Use the tabs |
| The sudo fails | The > redirect | Use the sudo tee |
| The here string no newline | The <<< appends | The expected |
| The nested expansion | The unquoted | Use the $() |
Real-World Examples
1. The SQL Script
mysql << EOF
SELECT * FROM users;
EOF
2. The SSH Command
ssh host << EOF
cd /app
git pull
EOF
3. The Config Generation
cat > config.conf << EOF
host=$HOST
port=$PORT
EOF
4. The Literal Script
cat > script.sh << 'EOF'
#!/bin/bash
echo "$HOME"
EOF
5. The Privileged Write
sudo tee /etc/file << EOF
content
EOF
6. The Here String
grep 'pattern' <<< "$var"
7. The JSON
jq '.name' <<< '{"name": "Alice"}'
8. The Read
read -r var <<< "value"
9. The Loop
while read -r line; do echo "$line"; done << EOF
a
b
EOF
10. The Complete
cat > /etc/app.conf << 'EOF'
key=$LITERAL
EOF
Visual
The Here Document Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE HERE DOCUMENT FLOW โ
โ โ
โ cat << EOF โ
โ line 1 โ
โ line 2 โ
โ line 3 โ
โ EOF โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The shell reads the lines until the EOF. โ โ
โ โ The lines become the stdin of the cat. โ โ
โ โ The cat prints them to the stdout. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ The here document is the inline stdin. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Quoted vs the Unquoted
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE QUOTED vs THE UNQUOTED โ
โ โ
โ THE UNQUOTED (`<< EOF`): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ NAME="Alice" โ โ
โ โ cat << EOF โ โ
โ โ Hello, $NAME โ โ
โ โ EOF โ โ
โ โ โ โ
โ โ The output: the Hello, Alice โ โ
โ โ The $NAME is the EXPANDED. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ THE QUOTED (`<< 'EOF'`): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ NAME="Alice" โ โ
โ โ cat << 'EOF' โ โ
โ โ Hello, $NAME โ โ
โ โ EOF โ โ
โ โ โ โ
โ โ The output: the Hello, $NAME โ โ
โ โ The $NAME is the LITERAL. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Privileged Write
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE PRIVILEGED WRITE โ
โ โ
โ โ THE `>` REDIRECT: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ sudo cat > /etc/file << EOF โ โ
โ โ โ the shell opens the /etc/file as the USER โ โ
โ โ โ the permission denied โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โ
THE `sudo tee`: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ sudo tee /etc/file << EOF โ โ
โ โ โ the tee runs as the ROOT โ โ
โ โ โ the success โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Here String
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE HERE STRING โ
โ โ
โ grep 'pattern' <<< "the string to search" โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The here string is the shorthand for: โ โ
โ โ echo "the string to search" | grep 'pattern' โ โ
โ โ โ โ
โ โ The difference: โ โ
โ โ The here string is the faster (the no subshell). โ โ
โ โ The here string appends the newline. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| The here document | The << operator |
| The quoted delimiter | The << 'EOF' (the no expansion) |
| The unquoted delimiter | The << EOF (the expansion) |
| The tabs stripped | The <<- |
| The here string | The <<< operator |
| The delimiter | The arbitrary word, the EOF the convention |
| The expansion | The $variable and the $(command) |
| The privileged write | The sudo tee |
| The here string vs the echo | The no subshell |
Key takeaways:
- The here document is the
<<operator that feeds the multiline input to the command. Thecat << EOFreads the lines until theEOF, and the lines become the standard input. The temporary file is the avoided. - The quoted delimiter (
<< 'EOF') prevents the variable expansion. The$variableand the$(command)are the literal. The unquoted delimiter (<< EOF) allows the expansion. - The
<<-strips the leading tabs. The delimiter can be the indented with the tabs, and the spaces are not the stripped. The<<-is the important for the script indentation. - The here string is the
<<<operator that feeds the single line. Thegrep 'pattern' <<< "$var"is the shorthand for theecho "$var" | grep 'pattern', and it avoids the subshell. - The here document is the SQL, the SSH, and the config generation. The
mysql << EOF, thessh host << EOF, and thecat > config << EOFare the patterns. - The
sudo teewrites the privileged file. Thesudo cat > /etc/filefails, because the redirection runs in the user’s shell. Thesudo tee /etc/file << EOFworks. - The here string appends the newline. The
wc -c <<< "abc"returns the 4, because the newline is the added. - The result is the inline input. The here document and the here string are the temporary file and the echo pipe the avoided. The result is the clean and the efficient shell scripting, which is the foundation of the configuration and the automation.
Remember: The here document is the << operator that feeds the multiline input, and the here string is the <<< operator that feeds the single line. The quoted delimiter prevents the expansion, and the unquoted allows it. The <<- strips the tabs. The result is the inline input, which is the foundation of the shell scripting.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!