Step 1 of 5 · Reading · ~3 min
Learn
Strings and Output
printf and Heredocs
echo got you this far, and now it's time to meet its limits — and the two tools that replace it when output gets serious: printf for formatted lines, heredocs for blocks. Between them, they cover every "produce exactly this text" job a script will ever have.
echo's dirty secret
echo is fine until it isn't: its behavior with flags and escapes differs between systems and shells (echo -n, echo -e — sometimes flags, sometimes printed literally; a portability mess with decades of history). And it can't format: no padding, no decimal control, no zero-fill. The professional default in scripts that matter is:
printf: C's workhorse, shell edition
printf "Hi, %s. You're %d.\n" "$name" "$age"
Same format-string idea as C/Go (this platform teaches it thrice — it's that foundational): %s string, %d integer, %% a literal percent. Two bash-specific facts to pin:
- No automatic newline.
printf "x"leaves the cursor hanging; every line you want ends in an explicit\n. (This makesprintfthe clean way to print without a newline, too — noecho -nportability roulette.) - The format recycles. Extra arguments re-run the format:
printf "%s\n" a b cprints three lines. That's a genuinely useful idiom — one-per-line output of a list in a single command.
Width and precision work as in C:
printf "%5d|\n" 42 # 42| — right-aligned, width 5
printf "%-5d|\n" 42 # 42 | — left-aligned
printf "%05d\n" 42 # 00042 — zero-padded
printf "%.2f\n" 3.14159 # 3.14 — yes, printf CAN format decimals…
…which deserves a callout given last chapter's news: bash arithmetic can't produce floats, but printf can format float-shaped text it's handed — the missing piece when bc/awk output needs tidying.
Heredocs: multi-line output without a hundred echos
When a script needs to emit a block — usage text, a config file, a report template — the heredoc feeds literal lines as stdin:
cat << EOF
Usage: $0 [options] <file>
Processes <file> and reports totals.
Run on $(date).
EOF
<< EOF says "everything until a line containing exactly EOF is input" (the delimiter's name is convention, not keyword). Variables and command substitutions expand inside — the block above interpolates the script name and date. Two variations complete the set:
<< 'EOF'(quoted delimiter) — expansion OFF; every$is literal. For emitting code or configs that contain their own$variables.<<- EOF— strips leading tabs, letting the block indent with your code.
Heredocs are everywhere in real operations: embedded SQL for psql, YAML for kubectl apply -f -, generated systemd units. The pattern is always the same — a program that reads stdin, fed a template from the script itself.
The one-line cousin: here-strings
When the input is a single string rather than a block, <<< feeds it straight in:
grep -q "error" <<< "$line" # no pipe, no extra process
A here-string is a one-line heredoc, and it replaces the wasteful echo "$line" | grep ... idiom. Same family, three members: << a block, << 'EOF' a block with expansion off, <<< a single string.
Your exercise: Format Output
Produce a line with printf instead of echo. The method that works every time: write the target output down, then build the format string left to right, one % spec per value, checking each against the sample. If columns have to line up, count the width in the expected output and encode it (%-10s, %6d) rather than eyeballing spaces.
Two things the submission log says people forget. printf adds no newline, so a format that does not end in a newline escape produces a line the grader never sees terminated. And the values are arguments, not text inside the format -- a format string with the variable names spelled out in it prints those names.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…