printf Command: Tutorial & Examples
The reliable way to print formatted output in the shell — predictable where echo is full of surprises.
What It Is
printf prints text according to a format string, filling in placeholders with the arguments you give it. If you've written C, it's the same idea and nearly the same syntax. In the shell it's the grown-up replacement for echo: where echo's behaviour around newlines, escapes, and flags quietly differs between systems and shells, printf does exactly what its format string says, everywhere. Once you've been bitten by echo eating a backslash or printing -n as text, you switch to printf for anything that matters and never look back.
It exists both as a shell builtin and as a coreutils program (/usr/bin/printf), so it's on every Linux machine. It only writes to output — nothing destructive — which makes it safe to experiment with freely. This page will make you fluent in the handful of format codes that cover 95% of real use, and show you the one genuinely surprising thing it does that turns it from "fancy echo" into a small power tool.
Your First Look
The simplest useful form — print a string and a newline:
printf '%s\n' "hello"
hello
Two things are already different from echo, and both are deliberate. First, printf does not add a newline for you — you write \n yourself. That feels like extra work until you realise it means you're always in control: no surprise trailing newline, no missing one. Second, the format string and the data are separate — %s is a slot, "hello" is what goes in it. That separation is the whole design, and it's what makes the next example click:
printf '%-10s|%5d\n' "name" 42
name | 42
The format said: a left-justified string in a 10-wide field, a bar, then a number right-justified in a 5-wide field, then a newline. You get neat columns with zero effort — try that with echo.
How I Read It (the format string)
A printf format string is just text with two kinds of special sequences sprinkled in. Read it left to right and it's completely predictable:
%-codes are placeholders — each one consumes one argument and formats it.%sfor a string,%dfor an integer,%ffor a float, and so on.\-escapes are literal characters you can't easily type —\nnewline,\ttab,\\a real backslash.- Everything else prints verbatim.
So '%-10s|%5d\n' reads as: format-an-arg (string, left, width 10), a literal |, format-an-arg (integer, right, width 5), a newline. The numbers and dashes between % and the letter are the field spec — width, alignment, padding, precision — and that's where all the formatting power lives.
The Format Codes, Explained
The conversions you'll actually use (each consumes one argument):
%s— a string, printed as-is. Your workhorse.%d/%i— a signed integer.printf '%d\n' 42→42.%f— a floating-point number, six decimals by default;%.2frounds to two (printf '%.2f\n' 3.14159→3.14).%x/%X— hexadecimal (printf '%x\n' 255→ff);%ooctal;%bbinary (in some printfs).%c— a single character.%%— a literal percent sign.
The field spec, written between % and the letter, controls layout:
- Width —
%10spads to 10 columns (right-justified by default);%-10sleft-justifies. - Zero-pad —
%05dpads a number with leading zeros:printf '%05d\n' 42→00042(perfect forfile_007.jpg-style names). - Precision —
%.2f(decimals) or%.3s(truncate a string to 3 chars).
And the escapes for characters you can't type literally: \n newline, \t tab, \r carriage return, \\ backslash, \xNN a byte by hex code.
The Magic: One Format String Recycles Over All Your Arguments
Here's the feature that makes printf quietly brilliant and that almost nobody learns: if you give more arguments than the format string has placeholders, printf reuses the format string, again and again, until it runs out of arguments. Watch:
printf '%s\n' one two three
one
two
three
One %s\n, three arguments — so the format runs three times, each on its own line. This turns printf into a tiny, reliable list-formatter. Need a two-column table from a flat list?
printf '%-8s %s\n' alice admin bob user carol admin
alice admin
bob user
carol admin
The format has two slots, you fed it six arguments, so it looped three times and built a table. This recycling behaviour is why printf '%s\n' "${array[@]}" is the canonical, safe way to print a shell array one-per-line — it handles any number of elements with one format string. Once you see it, you start reaching for printf to format whole lists, not just single lines.
Reading It by Example
printf '%s' with no \n. Deliberately no newline — the cursor stays on the line. Useful for prompts (printf 'Continue? ') or building a line in pieces.
printf '%05d\n' 7 → 00007. Zero-padded numbers for sortable filenames or fixed-width IDs. A pile of frame_00001.png … frame_99999.png sorts correctly because of this.
printf '%.2f\n' "$price". Money and rounded values — %.2f guarantees two decimals where echo "$price" would print whatever was in the variable.
printf '%s\t%s\n' "$name" "$value" in a loop builds clean, tab-separated output that pastes straight into a spreadsheet or feeds awk.
printf '%(%Y-%m-%d)T\n' -1 (bash builtin) prints the current date with no date subprocess — a fast timestamp inside hot loops.
Cheat Sheet
printf '%s\n' "$x"— the safeechoreplacement (no surprises)printf '%s\n' "${arr[@]}"— print an array, one element per lineprintf '%-20s %s\n' "$k" "$v"— aligned two-column outputprintf '%05d\n' "$n"— zero-padded integer ·printf '%.2f\n' "$f"— fixed decimalsprintf '%x\n' "$n"— decimal → hex ·printf '%d\n' 0xff— hex → decimal (255)printf '%q\n' "$s"— shell-quote a string safely (bash) for reuse in scriptsprintf '%b\n' 'a\tb'— interpret backslash escapes in the argument (the closest thing toecho -e)
How You'll Actually Use It
In day-to-day shell work, printf shows up the moment output needs to be exact: aligned columns in a status script, a value with no trailing newline, a zero-padded counter, money to two decimals, or a list printed safely one-per-line. The rule of thumb most scripters settle on: use echo for a quick throwaway message, use printf the instant formatting or portability matters — which, in anything you'll run twice, is most of the time. It pairs naturally with awk and sed in pipelines, and inside a bash loop it's how you assemble tidy, machine-readable lines.
Gotchas
- No automatic newline.
printf 'hi'leaves the cursor on the same line — add\nyourself. This is a feature (you're in control), but it surprises every newcomer once. - Too few arguments fills with empties/zeros.
printf '%s %s\n' oneprintsone— the missing%sbecomes empty (and a missing%dbecomes0), it does not error. Count your placeholders. %-signs in data need escaping. To print a literal%, write%%. Passing user data as the format string (printf "$user_input") is a real bug — stray%codes will misformat or read garbage. Always doprintf '%s' "$user_input".- Escapes live in the format string, not arguments.
\nworks inside the format; to interpret escapes in an argument you need%b. (echo -eblurs this;printfkeeps it honest.) - Builtin vs
/usr/bin/printfdiffer slightly. Your shell's builtin wins; the coreutils binary has its own quirks (e.g. number locale). Rarely matters, occasionally does.
History & Philosophy
printf is older than Unix shells caring about it — it's the C library's formatting function (1970s, Kernighan and Ritchie) lifted almost verbatim into the shell, which is why anyone who's written C feels instantly at home. It exists because echo was never standardised: different systems disagreed about whether echo -n suppressed the newline or printed -n, whether \t became a tab or stayed two characters. That ambiguity made portable scripts a minefield, and printf was the cure — one tool whose output depends only on the format string, not on which Unix you're standing on.
The deeper idea it teaches is the separation of format from data — describe the shape of your output once, then pour values into it. That principle runs from C to Python's f-strings to every templating system you'll ever touch, and printf is where most people first meet it in the shell. Learn its dozen format codes and you've learned a pattern you'll recognise for the rest of your career.
See Also
- echo — the simpler, less predictable cousin printf replaces for real work
- awk — has its own printf, for formatting inside data pipelines
- sed — stream editing, often paired with printf-built input
- cat — when you just want to dump a file unformatted
- bash — where printf's array-recycling trick shines
- shell — builtin vs external printf, and why it matters
Formatting output by hand is the easy part — knowing your servers are healthy is the hard part.
CleverUptime turns every server's raw numbers into plain-language status you don't have to format or parse. See your own server's health in 30 seconds, no signup.