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