printf 命令详解与参数实测(Ubuntu 22.04)
在 Ubuntu 22.04(bash 5.1.16)逐项实测 printf:%s/%d/%i/%f/%x/%o/%e/%g/%c/%% 与宽度精度标志、%b/%q/%()T 扩展、缺参与格式复用、银行家舍入四工具对照、-v 与 -- 解析差异、内置与 coreutils 外部形态对比及 printf vs echo 选型,全部命令与回显真实可复现。
printf 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 printf 的每一个格式说明符与参数——%s/%d/%i/%f/%x/%X/%o/%e/%g/%c/%% 及宽度、精度、标志(%5s、%-10s、%.2f、%08.3f、%*d 等)、%b 与 %q 扩展、%()T 时间格式、-v 变量赋值、缺参与格式复用行为、浮点银行家舍入、内置与 /usr/bin/printf 外部差异、与 echo 的选型对比,全部命令与回显已在 Ubuntu 22.04(bash 5.1.16)实测通过。
一、环境信息
本文所有命令与回显均在以下环境逐条实测,读者在同类环境可直接复现:
- 操作系统:Ubuntu 22.04.5 LTS,架构 x86_64
- Shell:GNU bash,version 5.1.16(1)-release (x86_64-pc-linux-gnu)
- 外部 printf:GNU coreutils 8.32(
/usr/bin/printf,大小 51648 字节) - 系统默认 locale:POSIX(未设置
LANG;已装C.utf8、en_US.utf8,Unicode 输出实验会显式设置) - 配套对照工具:awk(mawk)、python3、date(coreutils)
- 演示数据目录:
/root/demo/printf/(本文实测过程新建,含 lines.txt、table.txt、stamp.log)
二、命令概览
2.1 语法与测试数据
printf 按 FORMAT 格式串的控制把 ARGUMENTS 写到标准输出:格式串里含三类对象——普通字符原样复制、反斜杠转义序列转换后输出、格式说明符(如 %s)依次消费参数。与 echo 不同,printf 默认不追加换行,\n 必须显式写在格式串里。Ubuntu 22.04 下 printf 同样是「双形态」:
$ type printf
printf is a shell builtin
$ type -a printf
printf is a shell builtin
printf is /usr/bin/printf
printf is /bin/printf
$ type -t printf
builtin
$ command -V printf
printf is a shell builtin
$ which printf
/usr/bin/printf
内置 printf 的语法:printf [-v var] format [arguments];外部 GNU printf 的语法:printf FORMAT [ARGUMENT]...。本文演示数据统一放在 /root/demo/printf/:
$ mkdir -p /root/demo/printf
2.2 help 全量输出(内置用 help;外部形态用 --help)
内置命令用 help 查看,以下为 help printf 的完整原文(逐行照抄实测回显):
$ help printf
printf: printf [-v var] format [arguments]
Formats and prints ARGUMENTS under control of the FORMAT.
Options:
-v var assign the output to shell variable VAR rather than
display it on the standard output
FORMAT is a character string which contains three types of objects: plain
characters, which are simply copied to standard output; character escape
sequences, which are converted and copied to the standard output; and
format specifications, each of which causes printing of the next successive
argument.
In addition to the standard format specifications described in printf(1),
printf interprets:
%b expand backslash escape sequences in the corresponding argument
%q quote the argument in a way that can be reused as shell input
%(fmt)T output the date-time string resulting from using FMT as a format
string for strftime(3)
The format is re-used as necessary to consume all of the arguments. If
there are fewer arguments than the format requires, extra format
specifications behave as if a zero value or null string, as appropriate,
had been supplied.
Exit Status:
Returns success unless an invalid option is given or a write or assignment
error occurs.
外部形态用 --help 查看(GNU coreutils 版说明,全文照抄):
$ /usr/bin/printf --help
Usage: /usr/bin/printf FORMAT [ARGUMENT]...
or: /usr/bin/printf OPTION
Print ARGUMENT(s) according to FORMAT, or execute according to OPTION:
--help display this help and exit
--version output version information and exit
FORMAT controls the output as in C printf. Interpreted sequences are:
\" double quote
\\ backslash
\a alert (BEL)
\b backspace
\c produce no further output
\e escape
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\NNN byte with octal value NNN (1 to 3 digits)
\xHH byte with hexadecimal value HH (1 to 2 digits)
\uHHHH Unicode (ISO/IEC 10646) character with hex value HHHH (4 digits)
\UHHHHHHHH Unicode character with hex value HHHHHHHH (8 digits)
%% a single %
%b ARGUMENT as a string with '\' escapes interpreted,
except that octal escapes are of the form \0 or \0NNN
%q ARGUMENT is printed in a format that can be reused as shell input,
escaping non-printable characters with the proposed POSIX $'' syntax.
and all C format specifications ending with one of diouxXfeEgGcs, with
ARGUMENTs converted to proper type first. Variable widths are handled.
NOTE: your shell may have its own version of printf, which usually supersedes
the version described here. Please refer to your shell's documentation
for details about the options it supports.
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/printf>
or available locally via: info '(coreutils) printf invocation'
💡 说明:两份 help 对比可见——
-v var与%()T是 bash 内置独有的扩展;外部 GNU printf 的 %q 采用 POSIX$''引用风格(实测输出单引号包裹,见 3.10),内置 %q 采用反斜杠转义风格。
2.3 版本信息
版本号用命令现场获取,实测回显如下:
$ bash --version | head -1
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
$ /usr/bin/printf --version | head -1
printf (GNU coreutils) 8.32
$ ls -l /usr/bin/printf
-rwxr-xr-x 1 root root 51648 Aug 25 15:12 /usr/bin/printf
三、参数逐项实测
3.1 基本用法与格式串复用
printf 最小用法:格式串 + 参数。格式串中的 \n 负责换行;参数多于格式说明符时格式串自动循环复用(细节见 3.12):
$ printf "Hello, printf\n"
Hello, printf
$ printf '%s\n' "Hello, printf"
Hello, printf
$ printf '%s\n' a b c
a
b
c
💡 要点:脚本习惯里更推荐第二种写法——格式串与数据分离(
'%s\n'固定,数据作参数),这样数据里即使出现%或反斜杠也不会被误解释(见 3.8、3.9 节实测)。
3.2 %s:字符串与宽度精度
%s 输出字符串,可加宽度(右对齐)、负号(左对齐)、精度(截断)。逐个实测(| 用来标出右边界):
$ printf '[%5s]\n' ab
[ ab]
$ printf '[%-10s]|\n' left
[left ]|
$ printf '[%10s]|\n' right
[ right]|
$ printf '[%.3s]\n' abcdef
[abc]
宽度不足时补空格、超出时按实际长度显示不截断;.3 精度把 abcdef 截成 abc。
💡 要点:
%5s右对齐补空格、%-10s左对齐、%.3s截断,三者可组合(如%10.3s)。注意宽度按「字节」而非「显示宽度」计算,中文字符串对齐会失真(实测见第五节 Q7)。
3.3 %d/%i:十进制整数与标志
%d 输出十进制整数,%i 与 %d 等价(沿袭 C 语言 integer 惯例)。宽度、零填充、正负号标志实测:
$ printf '%d\n' 42
42
$ printf '%i\n' -7
-7
$ printf '[%5d]\n' 42
[ 42]
$ printf '[%-5d]|\n' 42
[42 ]|
$ printf '[%05d]\n' 42
[00042]
$ printf '[%+d] [%+d]\n' 42 -42
[+42] [-42]
$ printf '[% d]\n' 42
[ 42]
$ printf '[%*d]\n' 6 42
[ 42]
%*d 的星号表示宽度从参数取(6 是宽度、42 是值),这是 C printf 的标准能力,bash 内置同样支持。
数值参数的解析陷阱实测——%d 遇到浮点、带空格串会报错但仍输出已解析部分;010 按八进制、0x1F 按十六进制解析:
$ printf '%s\n' 42
42
$ printf '%d\n' 3.7
/root/printf-test2.sh: line 3: printf: 3.7: invalid number
3
$ printf '%d\n' 0x1F
31
$ printf '%d\n' 010
8
$ printf '%d\n' ' 42 '
/root/printf-test2.sh: line 3: printf: 42 : invalid number
42
💡 要点:%d 不做四舍五入也不截断小数——
3.7直接报invalid number并输出到非法字符前的3;前导 0 会触发八进制解析(010得 8 而不是 10),0x前缀按十六进制。做数值输出前先保证参数是干净的十进制串,或用10#$var强制十进制。
3.4 %f:浮点与精度
%f 默认输出 6 位小数;精度控制小数位、宽度控制整体占位、0 前缀补零:
$ printf '%f\n' 3.14159
3.141590
$ printf '[%.2f]\n' 3.14159
[3.14]
$ printf '[%8.3f]\n' 3.14159
[ 3.142]
$ printf '[%08.3f]\n' 3.14159
[0003.142]
$ printf '[%5.2f]\n' 3.14159
[ 3.14]
$ printf '[%-8.1f]|\n' 2.25
[2.2 ]|
%.2f 之外的舍入规则(银行家舍入)是高频踩坑点,单列在 3.13 节实测。
3.5 %x %X %o:进制转换
%x/%X 输出十六进制(小/大写),%o 输出八进制:
$ printf '%x\n' 255
ff
$ printf '%X\n' 255
FF
$ printf '%o\n' 64
100
💡 要点:进制转换是「十进制 → 其他进制」方向;反向(ff → 255)要用算术展开
$((16#ff))或%d配0x前缀参数(printf '%d\n' 0x1F实测得 31,见 3.3 节)。
3.6 %e %g:科学计数与通用浮点
%e 输出科学计数法(默认 6 位小数尾数);%g 自动在普通与科学计数间选择:
$ printf '%e\n' 12345.678
1.234568e+04
$ printf '%g\n' 0.00001234
1.234e-05
$ printf '%g\n' 123456789.0
1.23457e+08
$ printf '%g\n' 100000.0
100000
💡 要点:%g 在数值范围适中时用普通表示(100000 原样),过大/过小自动切换科学计数并保留 6 位有效数字;需要固定精度时加精度(如
%.3g)。
3.7 %c:单字符
%c 只取参数的第一个字符,多出的字符被丢弃;也支持宽度标志:
$ printf '%c\n' A
A
$ printf '%c\n' ABC
A
$ printf '%c%c%c\n' ABC DE
AD
$ printf '[%2c]\n' A
[ A]
$ printf '[%-2c]|\n' A
[A ]|
最后一个例子:三个 %c 只有两个参数(ABC、DE),分别取首字符 A、D,第三个 %c 缺参输出空串(缺参行为见 3.11)。
3.8 %%:百分号本身
格式串里 % 是转换引导符,输出百分号必须写 %%;参数中的 % 则无需任何处理:
$ printf '进度: 90%%\n'
进度: 90%
$ printf '%d%%\n' 88
88%
$ printf '%s\n' 'percent inside arg: 100%'
percent inside arg: 100%
💡 要点:格式串末尾残缺的
%会触发printf: '%': missing format character报错(实测见第五节 Q4 与《echo 命令详解与参数实测(Ubuntu 22.04)》3.12 节对照实验)。这也是「格式串与数据分离」写法的价值——数据走 %s 参数,永远不用担心百分号。
3.9 %b:解释参数中的转义(与 %s 对比)
%s 对参数原样输出;%b 会把参数中的反斜杠转义序列解释后输出。逐项对比实测:
$ printf '%s\n' 'a\tb'
a\tb
$ printf '%b\n' 'a\tb'
a b
$ printf '%b\n' 'col1\tcol2\nline2'
col1 col2
line2
$ printf '%b\n' 'octal: \0101\0102\0103'
octal: ABC
$ printf '%b\n' 'hex: \x41\x42\x43'
hex: ABC
$ printf '%b\n' '\101'
A
格式串内的转义由 printf 直接解释(不需要 %b),八进制 \nnn、十六进制 \xHH 都支持:
$ printf "fmt octal: \101\102\103\n"
fmt octal: ABC
$ printf "fmt hex: \x41\x42\x43\n"
fmt hex: ABC
$ printf 'a\vb\n' | od -c
0000000 a \v b \n
0000004
$ printf 'alert\a' | od -c
0000000 a l e r t \a
0000006
%b 同样能携带 ANSI 颜色序列(cat -v 验证控制字节,终端实际显示紫色 PURPLE):
$ printf '%b\n' '\e[35mPURPLE\e[0m' | cat -v
^[[35mPURPLE^[[0m
\u Unicode 转义在内置 printf 的格式串与 %b 参数中都受 locale 限制:POSIX locale 下输出字面量(十六进制被转成大写回显),C.UTF-8 下正常输出中文:
$ printf '%s\n' 'unicode no-interpret: \u4e2d\u6587'
unicode no-interpret: \u4e2d\u6587
$ printf '%b\n' 'unicode: \u4e2d\u6587'
unicode: \u4E2D\u6587
$ printf "fmt unicode: \u4e2d\u6587"
fmt unicode: \u4E2D\u6587
$ LANG=C.UTF-8 bash -c "printf \"%b\n\" \"unicode: \u4e2d\u6587\""
unicode: 中文
💡 要点:%b 的定位是「参数里的转义也当真」——变量值来自用户/文件、其中可能含
\t\n字面量时才用它;常规数据输出用 %s 更安全。\u输出不了中文先查 locale(locale看 LC_CTYPE),这与 echo -e 的行为机理相同。
3.10 %q:shell 安全引用
%q 把参数转义成「可直接粘回 shell 复用」的形式,空格、通配符、引号都会被处理。内置与外部风格不同,对比实测:
$ printf '%q\n' 'hello world'
hello\ world
$ printf '%q\n' "it's"
it\'s
$ printf '%q\n' 'a*b'
a\*b
$ printf '%q\n' 'two spaces'
two\ \ \ spaces
$ printf '%q\n' $'tab\there'
$'tab\there'
$ VAR='two words'; printf '%q\n' "$VAR"
two\ \ words
$ /usr/bin/printf '%q\n' 'hello world'
'hello world'
💡 要点:内置 %q 用反斜杠逐字符转义(
hello\ world),含控制字符时输出$'...'形式($'tab\there');外部 coreutils %q 用 POSIX 单引号风格('hello world')。两者都能保证eval后语义不变,是拼接命令行、生成安全 shell 片段的利器(实战见 4.3)。
3.11 缺参行为:%d 补 0、%s 补空、%f 补 0.000000
参数比格式说明符少时不报错,缺省值按类型补:数值补 0、字符串补空串。内置与外部行为一致:
$ printf '%d\n'
0
$ printf '%s\n'
$ printf '%f\n'
0.000000
$ printf '[%s][%d][%f]\n'
[][0][0.000000]
$ printf '%s and %s\n' onlyone
onlyone and
$ /usr/bin/printf "[%s][%d]\n"
[][0]
$ /usr/bin/printf "%s and %s\n" onlyone
onlyone and
💡 要点:
help printf明文写有「fewer arguments than the format requires … behave as if a zero value or null string」——这是 POSIX 规定行为而非 bug。风险在于脚本传参漏掉时静默输出 0/空行,写脚本时应自行校验参数个数,或开启set -u让未定义变量尽早报错。
3.12 参数多于格式符:格式串循环复用
参数多于格式说明符时,格式串自动从头复用直到参数耗尽。这是 printf 替代循环输出的核心能力:
$ printf 'x=%d y=%d\n' 1 2
x=1 y=2
$ printf 'x=%d y=%d\n' 1 2 3 4
x=1 y=2
x=3 y=4
$ printf 'x=%d y=%d\n' 1 2 3
x=1 y=2
x=3 y=0
$ printf '%s|%s\n' a b c d e
a|b
c|d
e|
💡 要点:最后一组若参数数不是格式符个数的整数倍,最后一轮照样复用并按缺参规则补 0/空(
x=3 y=0、e|)。批量输出时让「格式符个数 × N = 参数个数」最干净,或干脆一个格式符配'%s\n'逐行打。
3.13 浮点舍入:银行家舍入实测(四工具对照)
%.0f 对 0.5、1.5、2.5 的结果不是「四舍五入」直觉值。bash 内置、外部 coreutils、awk、python3 四种实现实测结果完全一致:
$ printf '%.0f\n' 0.5 1.5 2.5
0
2
2
$ printf '%.0f\n' 3.5
4
$ printf '%.1f\n' 0.25 0.35
0.2
0.3
$ /usr/bin/printf "%.0f\n" 0.5 1.5 2.5
0
2
2
$ awk 'BEGIN { printf "%.0f\n", 0.5; printf "%.0f\n", 1.5; printf "%.0f\n", 2.5 }'
0
2
2
$ python3 -c "for v in (0.5, 1.5, 2.5): print(f'{v:.0f}')"
0
2
2
💡 技术拓展:十进制舍入策略不止「四舍五入」一种,常见策略对比——
策略 规则 0.5 → 1.5 → 2.5 → 四舍五入(half-up) 尾数 ≥5 进位 1 2 3 银行家舍入(half-even) 舍入到最近的偶数 0 2 2 截断 直接丢弃尾数 0 1 2 向上/向下取整 ceil / floor 1 / 0 2 / 1 3 / 2 printf 的 %f 走 C 库(glibc)的 IEEE 754 默认舍入即「银行家舍入」(round-half-even),本文实测 bash 内置、coreutils 8.32、awk、python3 四种实现结果一致(0.5→0、1.5→2、2.5→2、3.5→4;0.25→0.2、0.35→0.3)。金融/计费场景需要 half-up 时,应改用 bc 或十进制库显式处理,不要依赖 printf。
3.14 %()T:strftime 时间格式与 TZ 依赖
%()T 是 bash 扩展:把 epoch 秒(缺省/负数 -1 表示当前时刻)按 strftime(3) 格式输出。实测当前时间、epoch 0、指定秒:
$ printf '%(%F %T)T\n'
2026-10-15 21:29:46
$ printf '%(%F %T)T\n' -1
2026-10-15 21:29:46
$ printf '%(%F %T)T\n' 0
1970-01-01 00:00:00
$ printf '%(%Y-%m-%d)T\n' 86400
1970-01-02
$ printf '%(%F %T)T\n' 1700000000
2023-11-14 22:13:20
$ date -d @1700000000 "+%F %T"
2023-11-14 22:13:20
与 date -d @epoch 交叉验证结果一致。时区跟随系统 TZ 环境变量,实测同一 epoch 在不同 TZ 下的输出:
$ TZ=Asia/Shanghai printf "%(%F %T)T\n" 0
1970-01-01 08:00:00
$ TZ=UTC printf "%(%F %T)T\n" 0
1970-01-01 00:00:00
$ date -u -d @0 "+%F %T"
1970-01-01 00:00:00
外部 printf 没有 %()T 扩展,直接报错(这是区分双形态的标志之一):
$ /usr/bin/printf "%(%F)T\n" 0
/usr/bin/printf: %(: invalid conversion specification
💡 要点:%()T 输出的是「本地时区」时间——本环境默认 UTC 时 epoch 0 显示 00:00:00,
TZ=Asia/Shanghai下显示 08:00:00。服务器多时区协作时,日志时间戳建议统一TZ=UTC或显式标注时区。注意 date 的回显时间均为实测时刻值,读者复现时会得到自己环境的时间。
3.15 -v:输出赋给变量而不打印
-v var 把格式化结果直接赋给 shell 变量,省去命令替换子进程开销:
$ printf -v RESULT '%04d' 42
$ echo "$RESULT"
0042
$ printf -v MSG 'name=%s id=%d\n' alice 7
$ echo "$MSG"
name=alice id=7
💡 要点:
printf -v是 bash 扩展(外部 printf 无此选项)。生成零填充编号、格式化报错信息再赋值的场景比VAR=$(printf ...)更高效,且-v赋值时格式串末尾的\n不会写入变量(变量值干净)。
3.16 -- 与 -n 开头格式串的解析差异
内置 printf 认 --(其后内容即使以 -n 开头也当格式串),不认 -n(没有 -n 选项,直接报错);外部 coreutils printf 把第一个参数一律当格式串。四种组合实测:
$ printf '%s\n' -- '-n'
--
-n
$ printf '-n\n'
/root/printf-test.sh: line 3: printf: -n: invalid option
printf: usage: printf [-v var] format [arguments]
$ printf -- '-n\n'
-n
$ /usr/bin/printf '-n\n'
-n
$ /usr/bin/printf -- '-n\n'
-n
第一组:内置 printf 对 -- 之后的参数不做选项处理,-- 与 -n 都被当作 '%s\n' 的数据参数原样输出(两行 --、-n)。第二组:内置 printf '-n\n' 报 -n: invalid option;第三组加 -- 后正常输出。外部两种写法都直接当格式串。
💡 要点:内置 printf 只有
-v一个选项,格式串可能以-开头(如-n=%d)时,统一写printf -- '格式串' 参数...最稳妥,内置外部行为一致。
3.17 内置与外部 /usr/bin/printf 差异
用 enable -n printf 禁用内置后验证外部形态可用性:
$ enable -n printf
$ type printf
printf is /usr/bin/printf
$ printf "外部 printf: %s\n" "builtin 已禁用"
外部 printf: builtin 已禁用
$ enable printf
$ type printf
printf is a shell builtin
日常功能对比实测(%b、%*d、%08.3f 外部均支持;%q 风格不同;%b 的 \u 在 POSIX locale 下同样输出字面量):
$ /usr/bin/printf "%b\n" "\u4e2d\u6587"
\u4E2D\u6587
$ /usr/bin/printf '%q\n' 'hello world'
'hello world'
$ /usr/bin/printf '[%*d]\n' 5 42
[ 42]
$ /usr/bin/printf '[%08.3f]\n' 3.14159
[0003.142]
汇总差异:
| 对比项 | bash 内置 printf | /usr/bin/printf(coreutils 8.32) |
|---|---|---|
| -v var 变量赋值 | 支持 | 无 |
| %()T 时间格式 | 支持 | 不支持(报 invalid conversion specification) |
| %q 输出风格 | 反斜杠转义(hello\ world) | POSIX 单引号('hello world') |
| 缺参行为 | 补 0/空串 | 补 0/空串(一致) |
| 格式串复用 | 支持 | 支持(一致) |
-n 开头格式串 | 报 invalid option,需 -- | 直接当格式串 |
| 调用开销 | 零(shell 内部完成) | fork+exec 一个进程 |
3.18 printf vs echo:选型对比与性能
两者都在《echo 命令详解与参数实测(Ubuntu 22.04)》与本文实测过,选型对比如下:
| 维度 | printf | echo |
|---|---|---|
| 换行控制 | 默认不换行,\n 显式写在格式串 | 默认带换行,-n 去掉 |
| 格式能力 | 宽度/精度/填充/对齐/进制/时间全套 | 无(仅 -n/-e/-E 三个开关) |
| 可移植性 | POSIX 行为定义完整,sh/dash/bash 表现一致 | 各 shell 默认行为不同(dash 默认解释转义、bash 默认不解释,实测见 echo 篇 Q5) |
| 安全性 | printf '%s\n' "$V" 对任意取值安全 | 变量值为 -n/-ne/-nE 时被吞成选项,输出为空(echo 篇 3.13 实测) |
| 百分号 | 格式串需写 %%,残缺 % 报错 | 原样输出无风险 |
| 批量输出 | 一条命令格式复用搞定 | 需循环逐次调用 |
性能实测(2000 行输出,数值随机器浮动、量级稳定):
$ time bash -c "for i in {1..2000}; do printf \"%s\n\" \"\$i\" > /dev/null; done"
real 0m0.017s
user 0m0.010s
sys 0m0.007s
$ time bash -c "printf \"%s\n\" {1..2000} > /dev/null"
real 0m0.003s
user 0m0.001s
sys 0m0.002s
💡 要点:本文实测循环 printf 2000 次约 17ms,一条格式复用的 printf 约 3ms(约 5 倍差距),echo 循环同量级(echo 篇实测 16ms)。选型结论:脚本与批量输出一律 printf;echo 留给交互式简单提示、快速追加日志行。
四、实战示例
4.1 对齐表格输出(%-10s 与 %6.2f 组合)
用固定宽度格式生成对齐表格并落盘,表头用 %6s、数据行用 %6.2f 保持列对齐:
$ printf '%-10s|%6s|\n' Product Price > /root/demo/printf/table.txt
$ printf '%-10s|%6.2f|\n' Apple 3.5 >> /root/demo/printf/table.txt
$ printf '%-10s|%6.2f|\n' Banana 2.25 >> /root/demo/printf/table.txt
$ printf '%-10s|%6.2f|\n' Cherry 18 >> /root/demo/printf/table.txt
$ cat /root/demo/printf/table.txt
Product | Price|
Apple | 3.50|
Banana | 2.25|
Cherry | 18.00|
💡 说明:
%-10s左对齐 10 列、%6.2f右对齐 6 列两位小数,小数点自动对齐(3.50/2.25/18.00)。列宽取「最长内容 + 余量」,中文内容需按显示宽度另行处理(见 Q7)。
4.2 零填充编号与文件名生成
%0Nd 生成定长编号,配合格式复用一次产出整批文件名:
$ printf '%03d\n' 1 2 3 10 100
001
002
003
010
100
$ printf 'bak_%04d.tar\n' {1..3}
bak_0001.tar
bak_0002.tar
bak_0003.tar
💡 说明:
%03d宽度 3 超宽不截断(100 原样显示);{1..3}由 shell 展开后交给 printf 复用格式串。需要真正创建文件时可接| xargs touch;需要赋值给变量时用printf -v N '%04d' 7(3.15 节实测得 0042 同款用法)。
4.3 %q 构造可安全复用的 shell 片段
变量含空格等特殊字符时,%q 输出可直接回填到命令行/脚本而不失真:
$ NAME='alice xu'; printf 'user=%q\n' "$NAME"
user=alice\ xu
$ printf '%q\n' 'a*b'
a\*b
$ printf '%q\n' $'tab\there'
$'tab\there'
💡 说明:
user=alice\ xu粘回 shell 后仍是单值alice xu,a\*b不会触发通配展开,$'tab\there'还原真实制表符。生成eval用命令串、拼接 SSH 远程命令时,先 %q 再拼接可避免注入与变形。
4.4 批量多行输出替代循环 echo
一条 printf 替代循环,生成多行文本文件:
$ printf '%s\n' '第一行' '第二行' '第三行' > /root/demo/printf/lines.txt
$ cat /root/demo/printf/lines.txt
第一行
第二行
第三行
$ time bash -c "for i in {1..2000}; do printf \"%s\n\" \"\$i\" > /dev/null; done"
real 0m0.017s
user 0m0.010s
sys 0m0.007s
$ time bash -c "printf \"%s\n\" {1..2000} > /dev/null"
real 0m0.003s
user 0m0.001s
sys 0m0.002s
💡 说明:
printf '%s\n' 列表...的格式复用天然按行输出;2000 行场景 3ms 对 17ms 的差距在大批量、高频脚本里会累积成秒级差异。参数来自变量数组时写printf '%s\n' "$@"同样成立。
4.5 带时间戳的日志行(%()T 与演示数据落盘)
%()T 一条命令完成「取时间 + 格式化 + 输出」,比 $(date ...) 命令替换少一个子进程:
$ printf '%(%F %T)T log: service ok\n' > /root/demo/printf/stamp.log
$ cat /root/demo/printf/stamp.log
2026-10-15 21:29:46 log: service ok
$ ls -l /root/demo/printf
total 12
-rw-r--r-- 1 root root 30 Oct 15 21:29 lines.txt
-rw-r--r-- 1 root root 36 Oct 15 21:29 stamp.log
-rw-r--r-- 1 root root 76 Oct 15 21:29 table.txt
💡 说明:
%(%F %T)T缺省取当前时刻(等价传 -1);格式串内普通字符(log: service ok)原样输出。本文演示目录最终三个文件 30/36/76 字节,与 4.1/4.4 生成内容一致。时间值为实测时刻,复现时随当前时间变化。
五、注意事项与常见问题
Q1:printf 输出怎么没有换行?
printf 默认不追加换行符,这是它与 echo 最基本的差异。换行必须显式写在格式串里:printf '%s\n' "$V"。格式复用时 \n 写在格式串末尾,循环体只提供数据(本文 3.1、3.12 节实测)。
Q2:printf '%.0f\n' 0.5 为什么输出 0 而不是 1? %f 采用 C 库的银行家舍入(round-half-even):舍入到最近偶数。本文实测 0.5→0、1.5→2、2.5→2、3.5→4,且 bash 内置、coreutils、awk、python3 四种实现结果一致(3.13 节)。需要「四舍五入」语义时用 bc 或专用十进制处理,不要用 %.0f 硬凑。
Q3:%d 遇到 3.7、010、0x1F、带空格的 ' 42 ' 会怎样?
实测(3.3 节):3.7 报 invalid number 并输出 3(解析到非法字符为止);010 按八进制得 8;0x1F 按十六进制得 31;' 42 ' 报错后输出 42。结论:%d 只接受干净的十进制串(可带 0x/前导 0 触发进制解析),浮点请用 %f,拼接数据前先清洗。
Q4:格式串里的百分号怎么输出?
写 %%。参数里的 % 不受影响(printf '%s\n' '100%' 原样输出)。格式串末尾残缺的 % 报 printf: '%': missing format character 并输出已解析部分(echo 篇 3.12 节实测 printf "100%" 输出 100 并报错)。格式串与数据分离的写法可彻底规避此类问题。
Q5:格式串以 -n 开头报错怎么办?
内置 printf 会把 -n\n 当选项解析,报 printf: -n: invalid option;写 printf -- '-n\n' 即可(3.16 节实测)。外部 coreutils printf 无此问题(首参数一律当格式串)。脚本要同时兼容两种形态时,统一加 -- 最稳。
Q6:printf 的 \u 为什么输出不了中文?
受 locale 限制。本环境默认 POSIX locale,实测格式串与 %b 中的 \u4e2d\u6587 都输出字面量 \u4E2D\u6587(3.9 节);LANG=C.UTF-8 bash -c "printf '%b\n' 'unicode: \u4e2d\u6587'" 实测正常输出「中文」。排查顺序:locale 看 LC_CTYPE → 换 UTF-8 locale 或 locale-gen。注意外部 coreutils printf 在同样的 POSIX locale 下同样输出字面量(3.17 节实测)。
Q7:%5s 对中文字符串为什么对不齐?
宽度按字节计算:UTF-8 中文每字 3 字节,「中文」共 6 字节,超过 %5s 的 5 字节宽度故不补空格;实测 printf '[%5s]\n' 中文 输出 [中文]、[%4s] 同样不补,而 [%5s] 配 abc 补 2 空格(printf2 实测)。终端显示宽度(中文 2 列/字)与字节宽不同,需要表格对齐建议用 column -t 或按显示宽度计算的专用处理。
Q8:缺参输出 0/空行是 bug 吗?怎么防?
是 POSIX 规定的特性:参数少于格式符时按 0(数值)/空串(字符串)补齐(3.11 节实测 [%s][%d][%f] 缺参得 [][0][0.000000])。防呆手段:脚本入口校验参数个数;set -u 拦截未定义变量;关键数值输出前用正则或算术展开验证合法性。
六、参考链接
- POSIX printf 规范:https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
- man7.org POSIX printf 手册页:https://man7.org/linux/man-pages/man1/printf.1p.html
- GNU coreutils 手册 printf 章节:https://www.gnu.org/software/coreutils/manual/html_node/printf-invocation.html
- GNU Bash 参考手册(Bash Builtins / printf):https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html
- C 库 printf(3) 格式说明符参考:https://man7.org/linux/man-pages/man3/printf.3.html
- strftime(3) 时间格式符(%()T 使用的格式集):https://man7.org/linux/man-pages/man3/strftime.3.html
- 本机离线手册:
man bash(SHELL BUILTIN COMMANDS / printf 段)、man printf(coreutils 版)、help printf