Heredocs & Here-Strings

Multi-line input via heredoc, single-line input via here-string, and script generation with tee << 'EOF'.

Heredocs and Here-Strings

Heredoc Basics

Standard heredoc — multi-line input
cat << 'EOF'
line one
line two with $VAR unexpanded
EOF
# Single-quoted 'EOF' prevents variable expansion
# Output:
# line one
# line two with $VAR unexpanded
Unquoted EOF — variables expand at write time
NAME="Evan"
cat << EOF
Hello $NAME, you are in $(pwd)
EOF
# Output: Hello Evan, you are in /home/evanusmodestus/atelier/...
Indented heredoc with <← (tabs stripped)
if true; then
    cat <<- EOF
	This line has a leading tab — stripped by <<-
	Spaces are NOT stripped, only tabs
	EOF
fi
# <<- strips leading TABS only (not spaces)
# Useful inside indented if/for blocks

Here-Strings

Feed a string directly to stdin
cat <<< "hello from here-string"
# Output: hello from here-string
# No need for echo | — here-string is cleaner

# Useful with read:
read first last <<< "Evan Modestus"
echo "$first"    # Evan
echo "$last"     # Modestus
Combine with tools
grep -o '[0-9]\+' <<< "port-smtp: 25"
# Output: 25

wc -w <<< "one two three"
# Output: 3

Script Generation

Write a script to /tmp/ via tee + heredoc
tee /tmp/my-script.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
echo "Running as $USER at $(date)"
EOF
cat /tmp/my-script.sh     # inspect
bash /tmp/my-script.sh    # execute after review
Git commit with heredoc message
git commit -m "$(cat << 'EOF'
docs(codex): add tee and bash heredoc entries

Multi-line commit message via heredoc.
The $() captures cat's output as the -m argument.
EOF
)"
Write config files with sudo
sudo tee /etc/resolv.conf.d/lab.conf << 'EOF'
nameserver 10.50.1.90
search inside.domusdigitalis.dev
EOF
# tee runs as root via sudo, heredoc provides content

See Also

  • tee — stream splitting and script capture

  • Safe Workflows — the write/inspect/execute pattern