opscape

Text processing · bash builtin

printf

Print text and values through a format string, with precise control over spacing, numbers and escapes.

printf FORMAT [ARGUMENT...]

15 of 15 examples run in Debian 13.7 on 2026-10-31 · manual: printf(1)

printf prints its arguments according to a format string, the same idea as C's printf: ordinary characters are printed as they are, %s takes the next argument as a string, %d as an integer, %f as a decimal number, and \n, \t are escapes. It never adds a newline you did not ask for.

It is the reliable way to print in scripts. Unlike echo, its behavior does not change between shells or depending on what the data looks like: an argument of -n or one containing backslashes prints literally through %s. When there are more arguments than format specifiers, the format is reused until the arguments run out, which makes one-line loops like printf '%s\n' "${files[@]}" possible.

In bash printf is a builtin with two extras: -v var stores the result in a variable instead of printing it, and %q quotes a string so it can be pasted back into a shell. There is also a standalone /usr/bin/printf from coreutils, used by sh, find -exec and xargs.

Examples

Print a value inside a sentence

printf 'Hello, %s. You have %d new messages.\n' "$USER" 3
explain
output
Hello, dev. You have 3 new messages.

%s takes the first argument as a string and %d the second as an integer. The trailing \n is required: printf adds no newline on its own.

Print without a trailing newline

printf 'Working...'; printf ' done\n'
explain
output
Working... done

Both calls end up on one line because the first has no \n. This is the portable way to write a prompt or a progress message, where echo -n is not portable.

Reuse the format for every argument

printf '%s\n' apple 'cherry pie' banana
explain
output
apple
cherry pie
banana

There is one %s and three arguments, so the format is applied three times. Quoting keeps cherry pie as one argument. printf '%s\n' "${array[@]}" prints an array one element per line the same way.

Consume arguments in pairs

printf '%-8s -> %s\n' $(cat data/hosts.txt)
explain
output
web01    -> 10.0.0.11
web02    -> 10.0.0.12
db01     -> 10.0.0.21
cache01  -> 10.0.0.31

Each pass through the format uses two arguments. The unquoted $(cat ...) splits the file into words, so the four lines become eight arguments and four output lines. That word splitting is fine here only because no field contains spaces.

Align columns with widths

printf '%-10s|%6s|%-6s|\n' name qty price
printf '%-10s|%6d|%6.2f|\n' widget 12 4.5 gadget 3 19.99
output
name      |   qty|price |
widget    |    12|  4.50|
gadget    |     3| 19.99|

A number after % sets the minimum width, and - left-aligns. %6.2f is six characters wide with two decimals. Values are padded, never truncated, so a long value pushes the columns out of line.

Format numbers

printf '%05d | %.3f | %x | %o | %e | %+d\n' 42 3.14159 255 8 123456 7
explain
output
00042 | 3.142 | ff | 10 | 1.234560e+05 | +7

%05d pads with zeros, %.3f rounds to three decimals, %x is hexadecimal, %o octal, %e scientific notation and %+d always shows a sign.

Print a literal percent sign

printf 'disk usage: %d%%\n' 91
explain
output
disk usage: 91%

%% prints one %. A lone % followed by a letter would be taken as a format specifier.

Never put data in the format string

msg='Disk 100% done'
printf "$msg\n"
printf '%s\n' "$msg"
output
Disk 100 0one
Disk 100% done

In the first call the message is the format, and % d in it is a valid specifier (a space flag plus d). With no argument left it prints 0, eating the d of done. Pass data as an argument to %s, as the second call does, and it prints exactly.

Expand escapes inside an argument with %b

line='name\tqty'
printf '%s\n' "$line"
printf '%b\n' "$line"
output
name\tqty
name	qty

Escapes in the format are always interpreted, but %s prints its argument literally, backslashes included. %b interprets escapes such as \t in the argument, which is how to print a string that deliberately contains them.

Quote a string for reuse in the shell with %q

printf '%q\n' 'file with spaces.txt' "it's" $'tab\there'
explain
output
file\ with\ spaces.txt
it\'s
$'tab\there'

%q (bash and GNU coreutils) escapes a string so that the shell would read it back as the same single word. Useful for logging the exact command a script is about to run, or for building a command to pass to ssh. Strings with control characters come out in $'...' form.

%q is not available in sh

printf '%q\n' 'a b'
explain
outputexit 2
/bin/sh: 1: printf: %q: invalid directive

Under dash, the /bin/sh on Debian and Ubuntu, the builtin printf does not know %q and fails with status 2. In POSIX scripts quote by hand, or call /usr/bin/printf, whose GNU version supports %q.

Store the result in a variable with -v

printf -v stamp '%s-%03d' build 7
echo "artifact: $stamp.tar.gz"
output
artifact: build-007.tar.gz

In bash, -v name assigns the formatted string to a variable instead of printing it. It avoids a $(...) subshell and keeps trailing newlines, which command substitution strips.

Print the current date without calling date

printf 'today is %(%Y-%m-%d)T, epoch %(%s)T\n' -1 -1
explain
output
today is 2026-10-31, epoch 1793478032

bash's %(fmt)T formats a timestamp with strftime codes; -1 means now. It saves starting a date process in loops. This is bash-only.

Repeat a character

printf '%.0s-' {1..30}; printf '\n'
explain
output
------------------------------

Brace expansion produces 30 arguments; %.0s prints each with zero characters of precision, so only the - in the format appears, once per argument. A quick way to draw a separator line.

A non-numeric argument to %d is an error

printf '%d\n' 12abc
explain
outputexit 1
/bin/bash: line 1: printf: 12abc: invalid number
12

printf prints what it could parse (12), complains on stderr and returns status 1, so scripts can catch bad input. An empty argument for %d quietly prints 0.

Common pitfalls

  • printf "$var" treats the data as a format: any % or \ in it is interpreted. Always write printf '%s' "$var".

  • printf adds no newline. A missing \n leaves the shell prompt glued to the output.

  • Too few arguments are filled with empty strings or zeros, without any warning, so a mistyped format can print plausible-looking garbage.

  • %d does not accept decimals: printf '%d' 3.7 prints 3, complains, and returns 1. Use %.0f to round, and note it rounds halves to even (2.5 gives 2).

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • %q and -v are bash builtin features; %(...)T is bash-only. The standalone GNU printf also has %q; the macOS /usr/bin/printf does not.

  • The format escapes and %s %d %f %x %o %b %c are POSIX and work in dash, bash, zsh and BSD printf alike.

Options worth knowing

A selection, described in our own words. For the full list, see printf(1).

OptionWhat it does
-v VARAssign the output to shell variable VAR instead of printing (bash builtin).

Related