AWK 美化/友好输出
AWK 的输出,主要还是靠 print
和 printf
两个函数,而这两个函数,我们前面已经用过很多次了。
print
和 printf
主要从 C 语言 借鉴而来,而且继承了 C 语言 的输出格式。
本章剩下的内容,我们就来看看 printf
支持哪些输出格式吧。
printf 函数语法
printf fmt, expr-list
参数说明
参数 | 说明 |
---|---|
fmt |
字符串字面量,用于指定输出的格式 |
expr-list |
传递给 fmt 的参数 |
例如下面的 awk printf 函数用法
printf "%d\n", 18
%d\n
是输出格式,用于指定如何输出传递的参数,而 18
则是要输出的参数,用于按照一定的语法替换 fmt
中以 %
开始的格式化符。
%d
这种以 百分号(%) 开始的字符串又称之为 格式化符,由 百分号(%) 和其它预定义的字符构成。
printf 支持的转义字符
AWK 的 printf
函数从 C 语言 借鉴而来,那么它所支持的 转义字符 也基本从 C 语言借鉴而来。
因此,如果你熟悉 C 语言的 printf()
函数,那么对于 AWK 的转义字符也会得心应手。
新行(换行符)\n
AWK 中的 新行(换行符) \n
表示。
因此,如果你想要 Hello World
中的每一个单词一行
Hello World
那么你只需要将 Hello World
中的 空格( ' '
) 换成 \n
即可
[www.twle.cn]$ awk 'BEGIN { printf "Hello\nWorld\n" }'
运行上面的 awk 命令,输出结果如下
Hello World
后面那个
\n
是为了保证 终端( shell ) 的提示符在新的一行。
水平分隔符(制表符)\t
如果你想要两个或更多个字符串/字段之间有一定的间隔,一种方法是添加相当数量的 空格,比如我们想要 Hello
和 World
两个单词之间有四个空格,则可以直接使用四个空格分开
Hello World
另一种更简单的方法,就是使用 水平分隔符(制表符)\t
。
Hello\tWorld
一般情况下,一个 \t
的间隙相当于 2 个或 4 个空格,这取决于配置
我们来看一个范例
[www.twle.cn]$ awk 'BEGIN { printf "编号\t姓名\t部门\t年龄\n";printf "编号 姓名 部门 年龄\n";}'
运行上面的 awk 命令,输出结果如下
编号 姓名 部门 年龄 编号 姓名 部门 年龄
从上面的输出可以看出,在我这台电脑上,一个 \t
等于 4 个空格。
竖直(垂直)分隔符 \v
竖直(垂直)分隔符 \v
看起来有点和 水平分隔符(制表符)\t
类似,但是,前者的功能并不是在竖直方向拉出一定的空间,而是在 新行中空出上一行所占用的空间
简单的来说,就是制作一个 楼梯 效果,比如 "编号\v姓名\v部门\v年龄\n"
的作用,其实是
编号 姓名 部门 年龄
[www.twle.cn]$ awk 'BEGIN { printf "编号\v姓名\v部门\v年龄\n"}'
运行上面的 awk 命令,输出结果如下
编号 姓名 部门 年龄
退格符 \b
退格符 \b
的作用,就是在之前的字符串基础上,往回退一个 字符。从某些方面说,就是删除一个前面的字符。
比如 Hello\bWorld
输出的效果就是 HellWorld
,也就是 \b
之前的那个 o
被删除了。
为了更逼真的演示 \b
的作用,请看下面这个范例
[www.twle.cn]$ awk 'BEGIN { printf "Field 1Field 2Field 3Field 4\n" }' [www.twle.cn]$ awk 'BEGIN { printf "Field 1\bField 2\bField 3\bField 4\n" }'
运行上面的 awk 命令,输出结果如下
Field 1Field 2Field 3Field 4 Field Field Field Field 4
从输出结果中可以看出,每一个 \b
都会用于删除前一个字符和 \b
本身
回车符 \r
在很多地方,或许你也经常听到 回车换行,也就是 \r\n
,虽然 回车\r
和 换行(\n)
的效果看起来类似,但它们实实在在的有点区别的。
- 换行符(
\n
) 是在 新行 继续输出 - 回车符(
\r
) 则是删除当前行的所有已经输出,然后回到当前行的开始重新输出,从某些方面说,就是从在当前从头开始输出。 - 回车换行(
\r\n
) 的意思就是回到当前行的开始并且在新行输出,从某些方面说,其实就是 换行
如果你比理解,请反复理解下面这个范例
[www.twle.cn]$ awk 'BEGIN { printf "Field 1\nField 22\nField 333\nField 4444\n\n" }' [www.twle.cn]$ awk 'BEGIN { printf "Field 1\rField 22\rField 333\rField 4444\n\n" }' [www.twle.cn]$ awk 'BEGIN { printf "Field 1\r\nField 22\r\nField 333\r\nField 4444\n" }'
运行上面的 awk 命令,输出结果如下
Field 1 Field 22 Field 333 Field 4444 Field 4444 Field 1 Field 22 Field 333 Field 4444
从结果中可以看出,\r\n
和 \n
的作用类似,而 \r
则是实打实的从当前行开始输出
换页符 (\f
)
换页符 (\f
) 顾名思义,就是 走纸换页,一般在 打印机打印 时才会生效,用于从一个 新页 开始输出。
如果不是在 打印机,那么输出效果和 竖直分割符(\v
) 的效果类似
[www.twle.cn]$ awk 'BEGIN { printf "Sr No\fName\fSub\fMarks\n" }' [www.twle.cn]$ awk 'BEGIN { printf "Sr No\vName\vSub\fMarks\n" }'
运行上面的 awk 命令,输出结果如下
Sr No Name Sub Marks Sr No Name Sub Marks
printf 支持的所有格式化符
AWK 的 printf
函数从 C 语言 借鉴而来,那么它所支持的 格式化符 也基本从 C 语言借鉴而来。
因此,如果你熟悉 C 语言的 printf()
函数,那么对于 AWK 的格式化符也会得心应手。
单个字符 %c
如果你想要将一个数字输出为 单个字符,可以使用 %c
格式化符。
需要注意的是
- 如果传递的是数字,这个数字必须是 ASCII 所支持的,也就是说 小于 128。
- 如果传递的是字符串,那么只有 第一个字符 会被输出,其它的则直接省略。
[www.twle.cn]$ awk 'BEGIN { printf "ASCII value 65 = character %c\n", 65 }' [www.twle.cn]$ awk 'BEGIN { printf "ASCII value 156 = character %c\n", 156 }' [www.twle.cn]$ awk 'BEGIN { printf "ASCII value twle = character %c\n", "twle" }'
运行上面的 awk 命令,输出结果如下
ASCII value 65 = character A ASCII value 156 = character ? ASCII value twle = character t
从输出结果中可以看出,如果传递的数字不能转换为 ASCII 字符,则直接输出一个 问号(?
)
整数 %d
和 %i
%d
和 %i
格式化符的作用一样,把传递的参数转换为 整数 然后输出。
需要注意的是:
- 如果传递的是 浮点数(小数),那么只会输出 整数部分,小数部分直接忽略。
- 如果传递的是 非整数 ,则直接输出
0
- 如果传递的是 字符串整数,则会转换为 整数 然后输出。
[www.twle.cn]$ awk 'BEGIN { printf "价格 = %d\n", 80 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %d\n", 80.66 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %d\n", "www.twle.cn" }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %d\n", "80.66" }'
运行上面的 awk 命令,输出结果如下
价格 = 80 价格 = 80 价格 = 0 价格 = 80
科学计数法 %e
和 %E
%e
和 %E
的作用一样,把传递的参数先用 科学计数法 表示然后输出。它们的差别在于 %E
会把科学计数法表示中的所有字母都大写。
需要注意的是:
不管传递的是什么,都会先转换成科学计数法。如果传递的是字符串,首先会转换成 0
然后再转换为科学计数法
[www.twle.cn]$ awk 'BEGIN { printf "价格 = %e\n", 80 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %e\n", 80.6699999 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %e\n", "www.twle.cn" }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %e\n", "80.66" }'
运行上面的 awk 命令,输出结果如下
价格 = 8.000000e+01 价格 = 8.067000e+01 价格 = 0.000000e+00 价格 = 8.066000e+01
%E
会把科学计数法中的所有字母都大写。
[www.twle.cn]$ awk 'BEGIN { printf "价格 = %E\n", 80 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %E\n", 80.6699999 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %E\n", "www.twle.cn" }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %E\n", "80.66" }'
运行上面的 awk 命令,输出结果如下
价格 = 8.000000E+01 价格 = 8.067000E+01 价格 = 0.000000E+00 价格 = 8.066000E+01
浮点数格式化符 %f
浮点数格式化符 %f
会把传递的参数先转换为 浮点数(小数),然后输出。
需要注意的是:
- 不管传递的是什么,都会先转换成浮点数。如果传递的是字符串,首先会转换成
0
然后再转换为浮点数 - 转换的时候,如果小数位数太多(默认多余 6 位)则会触发四舍五入
[www.twle.cn]$ awk 'BEGIN { printf "价格 = %f\n", 80 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %f\n", 80.6699999 }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %f\n", "www.twle.cn" }' [www.twle.cn]$ awk 'BEGIN { printf "价格 = %f\n", "80.66" }'
运行上面的 awk 命令,输出结果如下
价格 = 80.000000 价格 = 80.670000 价格 = 0.000000 价格 = 80.660000
格式化符 %g
和 %G
%g
和 %G
的作用是一样的,会根据传递的参数选择 %e
或 %f
格式化符,具体选哪个,取决于转后后的结果谁更短(位数更少)。
而 %G
和 %g
的区别,就是前者会把转换后的参数中的所有字母都大写。
- 当给定的数字去掉首尾的空格后, 整数 部分总位数小于等于 6 时,使用的是
%f
和%F
模式,且总共只保留 6 位数字,还会发生四舍五入。
格式化符 | 说明 | 范例 | 范例结果 |
---|---|---|---|
%g |
浮点数格式输出 | printf "%g",000123.456789213 |
123.457 |
%G |
浮点数格式输出且大写 E | printf "%G",000123.456789213 |
123.456789 |
- 当给定的数字去掉首尾的空格后, 整数 部分总位数大于 6 时,采用的是
%e
和%E
模式,且会去掉首尾的空格,并保留 6 位有效小数
格式化符 | 说明 | 范例 | 范例结果 |
---|---|---|---|
%g |
科学计数法输出 | printf "%g",0005678123.456789213 |
5.67812e+06 |
%G |
科学计数法输出且大写 E | printf "%G",0005678123.456789213 |
123.456789 |
八进制 %o
%o
用于输出一个 无符号八进制数字,准确的说是将数据先转换为无符号八进制数据然后输出
[www.twle.cn]$ awk 'BEGIN { printf "Octal representation of decimal number 10 = %o\n", 10}'
运行以上 awk 命令,输出结果如下
Octal representation of decimal number 10 = 12
%u
格式化符 %u
用于输出一个 无符号十进制数字,准确的说是将数据先转换为无符号十进制数据然后输出
[www.twle.cn]$ awk 'BEGIN { printf "无符号 10 = %u\n", 10 }'
运行以上 awk 命令,输出结果如下
无符号 10 = 10
而对于小于 0 的数据,则会先转换为无符号十进制数字再输出,转换方式为先抛弃小数,然后求补码。s
[www.twle.cn]$ awk 'BEGIN { printf "无符号 -11.332 = %u\n", -11.332 }'
运行以上 awk 命令,输出结果如下
无符号 -11.332 = 18446744073709551605
%s
格式化符 %s
用于输出一个字符串。准确的说是将数据先转换为字符串,然后输出
[www.twle.cn]$ awk 'BEGIN { printf "年龄 = %s\n", 28.1119 }'
运行以上 awk 命令,输出结果如下
年龄 = 28.1119
当然了,%s
还可以原样输出传递的字符串,例如
[www.twle.cn]$ awk 'BEGIN { printf "网站 = %s\n", "https://www.twle.cn/"}'
运行以上 awk 命令,输出结果如下
网站 = https://www.twle.cn/
%x and %X
格式化符 %x
和 %X
可以用来将一个 大于 0 的十进制 格式化为 十六进制
而 %X
和 %x
的区别,就是 %X
格式化生成的十六进制是大写字母,而 %x
则是小写字母
[www.twle.cn]$ awk 'BEGIN { printf "15 转换为十六进制为: = %x\n", 15}'
运行以上 awk 命令,输出结果如下
15 转换为十六进制为: = f
如果我们把 %x
改成 %X
则会输出大写的十六进制
[www.twle.cn]$ awk 'BEGIN { printf "15 转换为十六进制为: = %X\n", 15}'
运行以上 awk 命令,输出结果如下
15 转换为十六进制为: = F
输出百分号 %%
已经学习了这么多的格式化符,我们知道,任何格式化符都是以 百分号(%) 开始的
那么,如果我们要输出 % 又要怎么做呢?
答案就是: 和 C 语言 一样,使用 两个百分号(%%) 来输出一个 %
[www.twle.cn]$ awk 'BEGIN { printf "百分比 = %d%%\n", 80.66 }'
运行上面的 awk 命令,输出结果如下
百分比 = 80%
百分号(%) 的其它格式化符号
百分号(%) 还有一些其它非常有用的格式化组合,下面我们就来讲讲几个比较常见的
指定占用宽度
AWK 使用 printf
输出时,默认是参数多长就输出多长,不会浪分一丁点空间。
但如果我们想要位某个参数指定固定的长度,则可以在 百分号(%) 后面,格式化字符之前添加指定的 长度。
例如,对于 %d
如果我们要指定占用宽度为 10
,则可以使用格式化符 %10d
。
默认情况下,当输出是 右对齐的,且左边默认填充 空格(' '
)。
[www.twle.cn]$ awk 'BEGIN { num1 = 10; num2 = 20; printf "Num1 = %10d\nNum2 = %10d\n", num1, num2 }'
运行上面的 awk 命令,输出结果如下
Num1 = 10 Num2 = 20
左对齐
在我们指定了宽度的前提下,AWK 有条默认的规则: 当输出的数字不足以填满宽度时,默认是右对齐的。例如下面的范例
[www.twle.cn]$ awk 'BEGIN { num1 = 10;num2 = 12356; printf "Num1 = %7d\nNum2 = %7d\n", num1,num2 }' | cat -vte
输出结果如下
Num1 = 10$ Num2 = 12356$
上面的范例,有两个地方值得注意:
- 输出默认时左对齐的,因此
Num1 =
和Num2 =
垂直对齐且左对齐 - 当指定了宽度,默认时右对齐的。
10
和123456
就时右对齐。
如果我们想要输出的数字不足以填满宽度时仍然左对齐,则需要在百分号后类型之前面添加 负号(-)。
对于上面的范例,就是将 %7d
改成 %-7d
[www.twle.cn]$ awk 'BEGIN { num1 = 10;num2 = 12356; printf "Num1 = %-7d\nNum2 = %-7d\n", num1,num2 }' | cat -vte
运行上面的范例,输出结果如下
Num1 = 10 $ Num2 = 12356 $
前缀的 0
不知道你有没有留意上一条的数组结果。哈哈,去翻翻~~~~
在我们指定了宽度的前提下,AWK 还有另一条默认的规则: 当输出的数字不足以填满宽度时,默认右对齐的,且使用空格补足剩余部分。
例如下面的范例
[www.twle.cn]$ awk 'BEGIN { num1 = 10;num2 = 12356; printf "Num1 = %7d\nNum2 = %7d\n", num1,num2 }' | cat -vte
输出结果如下
Num1 = 10$ Num2 = 12356$
这时候,如果要将默认的 空格填充 改成 0
,比如 0
,则需要在 百分号(%) 后面,宽度数字之前 添加 0
,例如 %7d
改成 %07d
注意: 只能是
0
,如果是其它字符,默认是没有任何效果的。
[www.twle.cn]$ awk 'BEGIN { num1 = -10; num2 = 20; printf "Num1 = %07d\nNum2 = %07d\n", num1, num2 }'
运行上面的 awk 命令,输出结果如下
Num1 = -000010 Num2 = 0000020
数字前缀的正负号
AWK 支持输出时在数字前面添加 正负号(+-),也就是说,把 10.00
改成 +10.00
,把 -10.00
改成 -10.00
哈,其实负数没啥好添加前缀的,因为人家本来就有一个 负号
要在数字前面添加正负号,只需要在 百分号(%) 后类型之前添加一个 正号(+)即可,比如 %d
改成 %+d
,比如 %f
改成 %+f
[www.twle.cn]$ awk 'BEGIN { num1 = -10; num2 = 20; printf "Num1 = %+d\nNum2 = %+d\n", num1, num2 }'
输出结果为
Num1 = -10 Num2 = +20
比如
[www.twle.cn]$ awk 'BEGIN { num1 = -10.234; num2 = 20.456; printf "Num1 = %+f\nNum2 = %+f\n", num1, num2 }'
输出结果为
Num1 = -10.234000 Num2 = +20.456000
数字格式化
AWK 的输出还支持对数字进行格式化,比如八进制、十六进制、小数点、千分位等。具体的规则如下
- 如果给定的值不为 0,则支持八进制输出和十六进制输出
格式化符 | 说明 | 范例 | 范例结果 |
---|---|---|---|
%o |
八进制格式 | printf("%o",10) | 12 |
%#o |
八进制且附加前缀 0 |
printf("%#o",10) | 012 |
%x |
十六进制格式 | printf("%x",10) | a |
`%X | 大写十六进制格式 | printf("%X",10) | A |
%#x |
十六进制格式附加前缀 0x |
printf("%#x",10) | 0xa |
%#X|大写十六进制格式附加前缀 0X` |
printf("%#X",10) | 0XA |
如果给定的值为
0
则会直接输出 0 而无视各种格式符
- 对于任意数字,都是支持以下科学计数法输出
格式化符 | 说明 | 范例 | 范例结果 |
---|---|---|---|
%e |
科学计数法输出 | printf "%e",123.456789213 |
1.234568e+02 |
%E |
科学计数法输出且大写 E | printf "%E",123.456789213 |
1.234568E+02 |
%e
和 %E
并会移除前置的 0
,比如下面这个范例
awk 'BEGIN { printf "%e\n", 000001231.456789213}'
输出结果为
1.231457e+03
- 对于任意数字,都支持以下浮点数格式化符
格式化符 | 说明 | 范例 | 范例结果 |
---|---|---|---|
%e |
科学计数法输出 | printf "%f",123.456789213 |
123.456789 |
%E |
科学计数法输出且大写 E | printf "%F",123.456789213 |
123.456789 |
注意: Mac OS 自带的 awk 不支持
%F
%f
和 %F
并会移除前置的 0
,比如下面这个范例
awk 'BEGIN { printf "%f\n", 000001231.456789213}'
输出结果为
1231.456789
- 对于任意数字,还支持以下浮点数格式化符。
%g
和 %G
是 %e
、%E
、%f
、%F
的升级版。
-
当给定的数字去掉首尾的空格后, 整数 部分总位数小于等于 6 时,使用的是
%f
和%F
模式,且总共只保留 6 位数字,还会发生四舍五入。格式化符 说明 范例 范例结果 %g
浮点数格式输出 printf "%g",000123.456789213
123.457 %G
浮点数格式输出且大写 E printf "%G",000123.456789213
123.456789 -
当给定的数字去掉首尾的空格后, 整数 部分总位数大于 6 时,采用的是
%e
和%E
模式,且会去掉首尾的空格,并保留 6 位有效小数格式化符 说明 范例 范例结果 %g
科学计数法输出 printf "%g",0005678123.456789213
5.67812e+06 %G
科学计数法输出且大写 E printf "%G",0005678123.456789213
123.456789