printf
| Since: | POSIX(sh互換) |
|---|
The printf command in shell scripts formats and outputs strings, integers, floating-point numbers, and more using a format string. Unlike echo, it does not automatically append a newline, giving you complete control over the output. In addition to format specifiers such as %s, %d, %f, and %x, you can specify field widths and precision for padding and alignment. Because printf is defined by POSIX, the same syntax works across bash, sh, and zsh, making it especially useful when writing portable scripts.
printf vs echo
echo automatically appends a newline at the end, but its option behavior varies between shells and implementations. printf, on the other hand, fully specifies the output via the format string, offering superior portability and consistency.
| Comparison | echo | printf |
|---|---|---|
| Trailing newline | Added automatically. | Not added. Control it by including \n in the format string. |
| Escape sequences | Behavior varies by shell and options (e.g., whether -e is supported). | Always interprets \n, \t, \\, and other escapes in the format string. |
| Format specifiers | Not supported. | Supports %s, %d, %f, etc. to specify type and width. |
| Portability | The behavior of -e and -n is implementation-dependent. | POSIX-compliant, so it can be used safely with sh. |
| Repeating with multiple arguments | Concatenates all arguments and outputs them on one line. | When the number of arguments exceeds the number of format specifiers, the format string is applied repeatedly. |
# echo — a newline is automatically appended echo "碇シンジ" # echo -n — no newline (not supported in some shells) echo -n "碇シンジ" # printf — control newlines explicitly with \n printf "碇シンジ\n" # printf — output without a trailing newline printf "碇シンジ" # printf — repeating the format (when there are more arguments than format variables) # → loops twice for "碇シンジ" and "綾波レイ" printf "%s\n" "碇シンジ" "綾波レイ"
Format Specifiers
Format specifiers start with % and specify the type of value to output. Arguments are assigned to specifiers in the order they appear in the format string.
| Specifier | Type | Description |
|---|---|---|
%s | String | Outputs the argument as a string. |
%d | Decimal integer | Outputs the argument as a signed decimal integer. |
%i | Decimal integer | Same as %d. Also accepts hexadecimal input with a 0x prefix. |
%f | Floating-point number | Outputs the argument as a decimal floating-point number. Defaults to 6 decimal places. |
%e | Scientific notation | Outputs the argument in scientific notation (e.g., 1.234560e+02). |
%g | Floating-point (auto) | Automatically switches between %f and %e based on the magnitude of the value. |
%x | Hexadecimal (lowercase) | Outputs the argument as hexadecimal using lowercase letters (a–f). |
%X | Hexadecimal (uppercase) | Outputs the argument as hexadecimal using uppercase letters (A–F). |
%o | Octal | Outputs the argument as an octal number. |
%c | Character | Outputs only the first character of the argument. |
%% | Literal % | Outputs a literal percent sign. |
# %s — output as a string printf "%s\n" "碇シンジ" # %d — output as a decimal integer printf "%d\n" 42 # %f — output as a floating-point number (6 decimal places by default) printf "%f\n" 3.14 # %x — output as hexadecimal (lowercase) printf "%x\n" 255 # → ff # %X — output as hexadecimal (uppercase) printf "%X\n" 255 # → FF # %o — output as octal printf "%o\n" 8 # → 10 # %% — output a literal percent sign printf "Progress: %d%%\n" 80
Padding and Alignment
By specifying width, precision, and flags between % and the type character, you can fix the field width, set decimal precision, and align output in columns.
| Syntax | Description |
|---|---|
%10s | Outputs right-aligned in a field of 10 characters. Pads with spaces on the left. |
%-10s | Outputs left-aligned in a field of 10 characters. Pads with spaces on the right. |
%05d | Outputs with zero-padding in a field of 5 digits (e.g., 00042). |
%+d | Outputs positive numbers with a leading + sign. |
%.2f | Rounds the output to 2 decimal places. |
%8.2f | Outputs right-aligned in a total field width of 8 characters with 2 decimal places. |
%-8.2f | Outputs left-aligned in a total field width of 8 characters with 2 decimal places. |
%*d | Takes the field width from an argument (used as printf "%*d" width value). |
# Right-aligned (default) — field width 10, space-padded printf "%10s\n" "碇シンジ" # Left-aligned — field width 15, space-padded (- flag) printf "%-15s\n" "惣流・アスカ・ラングレー" # Zero-padding — integer in a field of 6 digits printf "%06d\n" 42 # → 000042 # Force a sign on the output printf "%+d\n" 42 # → +42 printf "%+d\n" -42 # → -42 # Fix to 2 decimal places printf "%.2f\n" 3.14159 # → 3.14 # Total field width 8, 2 decimal places printf "%8.2f\n" 3.14159 # → 3.14 # Left-aligned, total field width 8, 2 decimal places printf "%-8.2f|\n" 3.14159 # → 3.14 |
Portability
printf is specified by POSIX.1, so the same syntax works across a wide range of shells including sh, dash, and zsh, not just bash. The following points summarize best practices for writing portable scripts.
| Topic | Recommendation | Reason |
|---|---|---|
| Outputting a newline | printf "%s\n" "$var" | echo "$var" may not recognize -e in dash or some sh implementations. Using \n with printf is the safer choice. |
| Escape sequences | printf "line1\nline2\n" | echo -e is not in POSIX and is implementation-dependent. printf reliably interprets \n and \t. |
Strings starting with - | printf "%s\n" "-n" | echo -n is interpreted as an option, but with printf the value is part of the format string, so it works correctly. |
%b specifier | Avoid using it. | %b (backslash expansion) has inconsistent behavior across non-POSIX shells. |
| Floating-point input | Set LC_ALL=C | In some locales the decimal separator is a comma (,), which can affect how %f parses input. |
# ----------------------------------------------- # Portable printf usage # ----------------------------------------------- # Not recommended: echo -e behavior varies by shell # echo -e "line1\nline2" # Recommended: printf reliably interprets \n printf "line1\nline2\n" # Not recommended: echo -n is not in POSIX (may not work in some shells) # echo -n "no newline" # Recommended: simply omit the trailing \n in printf printf "no newline" # Recommended: strings starting with - are output safely printf "%s\n" "-option-like-string" # Recommended: use LC_ALL=C to avoid locale-dependent behavior LC_ALL=C printf "%.2f\n" 3.14
Sample Code
eva_printf_basic.sh
#!/bin/bash
# -----------------------------------------------
# Demonstrates the basic printf format specifiers
# using Evangelion pilot data
# -----------------------------------------------
# -----------------------------------------------
# Pilot data (name, unit, sync rate, ID code, serial number)
# -----------------------------------------------
# Name Unit Rate ID Serial
pilot1="碇シンジ"; eva1="初号機"; rate1=41.3; id1=3; serial1=1
pilot2="綾波レイ"; eva2="零号機"; rate2=57.2; id2=0; serial2=0
pilot3="惣流・アスカ・ラングレー"; eva3="弐号機"; rate3=98.0; id3=2; serial3=2
pilot4="渚カヲル"; eva4="第4使徒"; rate4=100.0; id4=17; serial4=17
pilot5="真希波・マリ・イラストリアス"; eva5="仮設5号機"; rate5=77.8; id5=5; serial5=5
echo "=== Evangelion Pilot Database ==="
echo ""
# -----------------------------------------------
# %s — output as a string
# -----------------------------------------------
printf "Pilot: %s\n" "${pilot1}"
printf "Pilot: %s\n" "${pilot2}"
echo ""
# -----------------------------------------------
# %d — output as a decimal integer
# -----------------------------------------------
printf "ID (decimal): %d\n" "${id3}"
printf "ID (decimal): %d\n" "${id4}"
echo ""
# -----------------------------------------------
# %f — output as a floating-point number (6 decimal places by default)
# -----------------------------------------------
printf "Sync rate: %f\n" "${rate1}"
echo ""
# -----------------------------------------------
# %.2f — fix to 2 decimal places
# -----------------------------------------------
printf "Sync rate (2dp): %.2f%%\n" "${rate1}"
printf "Sync rate (2dp): %.2f%%\n" "${rate3}"
printf "Sync rate (2dp): %.2f%%\n" "${rate4}"
echo ""
# -----------------------------------------------
# %x — output as hexadecimal (lowercase)
# %X — output as hexadecimal (uppercase)
# -----------------------------------------------
printf "Serial (hex lower): %x\n" "${serial4}" # 17 → 11
printf "Serial (hex upper): %X\n" "${serial4}" # 17 → 11
printf "Serial (hex lower): %x\n" "${serial5}" # 5 → 5
echo ""
# -----------------------------------------------
# %% — output a literal percent sign
# -----------------------------------------------
printf "Top sync rate: %d%%\n" 100
$ chmod +x eva_printf_basic.sh $ ./eva_printf_basic.sh === Evangelion Pilot Database === Pilot: 碇シンジ Pilot: 綾波レイ ID (decimal): 2 ID (decimal): 17 Sync rate: 41.300000 Sync rate (2dp): 41.30% Sync rate (2dp): 98.00% Sync rate (2dp): 100.00% Serial (hex lower): 11 Serial (hex upper): 11 Serial (hex lower): 5 Top sync rate: 100%
eva_printf_padding.sh
#!/bin/bash
# -----------------------------------------------
# Demonstrates printf padding and alignment
# using Evangelion pilot data
# -----------------------------------------------
# -----------------------------------------------
# Pilot list (name, unit, sync rate)
# -----------------------------------------------
declare -a pilots=("碇シンジ" "綾波レイ" "惣流・アスカ・ラングレー" "渚カヲル" "真希波・マリ・イラストリアス")
declare -a evas=("初号機" "零号機" "弐号機" "第4使徒" "仮設5号機")
declare -a rates=("41.30" "57.20" "98.00" "100.00" "77.80")
declare -a ids=(3 0 2 17 5)
echo "=== Pilot List (with alignment) ==="
echo ""
# Print the header row
# %-28s : left-aligned, 28 characters (name column)
# %-10s : left-aligned, 10 characters (unit column)
# %7s : right-aligned, 7 characters (sync rate header)
# %5s : right-aligned, 5 characters (ID header)
printf "%-28s %-10s %7s %5s\n" "Name" "Unit" "Rate" "ID"
printf "%-28s %-10s %7s %5s\n" "----------------------------" "----------" "-------" "-----"
# Loop over the number of array elements
for i in 0 1 2 3 4; do
# %-28s : name, left-aligned, 28 characters
# %-10s : unit, left-aligned, 10 characters
# %6.2f%% : sync rate, width 6, 2 decimal places, right-aligned
# %5d : ID, right-aligned, 5 digits
printf "%-28s %-10s %6.2f%% %5d\n" \
"${pilots[$i]}" "${evas[$i]}" "${rates[$i]}" "${ids[$i]}"
done
echo ""
echo "=== Zero-padding example (serial numbers in 6 digits) ==="
echo ""
# %06d — width 6, zero-padded
for i in 0 1 2 3 4; do
printf "Serial: %06d Pilot: %s\n" "${ids[$i]}" "${pilots[$i]}"
done
echo ""
echo "=== Signed output (+ flag) ==="
echo ""
# %+.1f — include a + sign for positive numbers
diff_rates=("41.3" "-12.5" "98.0" "100.0" "-5.2")
for i in 0 1 2 3 4; do
printf " %-28s Change: %+.1f%%\n" "${pilots[$i]}" "${diff_rates[$i]}"
done
$ chmod +x eva_printf_padding.sh $ ./eva_printf_padding.sh === Pilot List (with alignment) === 名前 搭乗機 適合率 ID ---------------------------- ---------- ------- ----- 碇シンジ 初号機 41.30% 3 綾波レイ 零号機 57.20% 0 惣流・アスカ・ラングレー 弐号機 98.00% 2 渚カヲル 第4使徒 100.00% 17 真希波・マリ・イラストリアス 仮設5号機 77.80% 5 === Zero-padding example (serial numbers in 6 digits) === Serial: 000003 Pilot: 碇シンジ Serial: 000000 Pilot: 綾波レイ Serial: 000002 Pilot: 惣流・アスカ・ラングレー Serial: 000017 Pilot: 渚カヲル Serial: 000005 Pilot: 真希波・マリ・イラストリアス === Signed output (+ flag) === 碇シンジ Change: +41.3% 綾波レイ Change: -12.5% 惣流・アスカ・ラングレー Change: +98.0% 渚カヲル Change: +100.0% 真希波・マリ・イラストリアス Change: -5.2%
eva_printf_portable.sh
#!/bin/sh
# -----------------------------------------------
# Uses #!/bin/sh as the shebang to demonstrate
# portable printf usage that works with POSIX sh
# -----------------------------------------------
# -----------------------------------------------
# Pilot data (arrays are not available in sh,
# so individual variables are used)
# -----------------------------------------------
pilot1="碇シンジ"; eva1="初号機"; rate1="41.30"
pilot2="綾波レイ"; eva2="零号機"; rate2="57.20"
pilot3="惣流・アスカ・ラングレー"; eva3="弐号機"; rate3="98.00"
pilot4="渚カヲル"; eva4="第4使徒"; rate4="100.00"
pilot5="真希波・マリ・イラストリアス"; eva5="仮設5号機"; rate5="77.80"
# -----------------------------------------------
# Portable output patterns
# Use printf instead of echo -e
# -----------------------------------------------
# Use printf "%s\n" to reliably append a newline
printf "%s\n" "=== NERV Pilot Emergency Bulletin ==="
printf "%s\n" ""
# Output multiple values on one line with format specifiers
# %-28s : name column (left-aligned, 28 characters)
# %s : unit name
# %.2f : sync rate (2 decimal places)
printf "%-28s / %s Sync rate: %.2f%%\n" "${pilot1}" "${eva1}" "${rate1}"
printf "%-28s / %s Sync rate: %.2f%%\n" "${pilot2}" "${eva2}" "${rate2}"
printf "%-28s / %s Sync rate: %.2f%%\n" "${pilot3}" "${eva3}" "${rate3}"
printf "%-28s / %s Sync rate: %.2f%%\n" "${pilot4}" "${eva4}" "${rate4}"
printf "%-28s / %s Sync rate: %.2f%%\n" "${pilot5}" "${eva5}" "${rate5}"
printf "%s\n" ""
# -----------------------------------------------
# Storing output in a variable
# (printf output can be captured via command substitution)
# -----------------------------------------------
# Assemble pilot info into a single string
report=$(printf "Pilot: %-28s Unit: %s Sync rate: %.2f%%" \
"${pilot1}" "${eva1}" "${rate1}")
printf "%s\n" "${report}"
printf "%s\n" ""
# -----------------------------------------------
# Safely output strings starting with -
# echo "-option" may be misread as an option by some shells
# -----------------------------------------------
option_str="-v3.33" # Version number string styled after Evangelion
# Potentially unsafe: echo "${option_str}"
# Safe: use printf "%s\n" to output it correctly
printf "%s\n" "${option_str}"
$ chmod +x eva_printf_portable.sh $ ./eva_printf_portable.sh === NERV Pilot Emergency Bulletin === 碇シンジ / 初号機 Sync rate: 41.30% 綾波レイ / 零号機 Sync rate: 57.20% 惣流・アスカ・ラングレー / 弐号機 Sync rate: 98.00% 渚カヲル / 第4使徒 Sync rate: 100.00% 真希波・マリ・イラストリアス / 仮設5号機 Sync rate: 77.80% Pilot: 碇シンジ Unit: 初号機 Sync rate: 41.30% -v3.33
Summary
The printf command in shell scripts gives you complete control over output through a format string. Unlike echo, it does not append a trailing newline automatically — you include \n in the format string to add one explicitly. The most commonly used format specifiers are %s (string), %d (decimal integer), %f (floating-point), %x/%X (hexadecimal), and %o (octal). Specifying a field width applies right-alignment by default; the - flag switches to left-alignment, and the 0 flag enables zero-padding — all useful for producing tabular output. The %.2f syntax fixes the number of decimal places. Because printf is POSIX-compliant, it works safely in #!/bin/sh scripts and is especially valuable when you want to avoid the implementation-dependent behavior of echo -e and echo -n. Related commands include read (reading standard input) and here documents (multi-line output).
If you find any errors or copyright issues, please contact us.