32、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.ddkk.com]$ awk 'BEGIN { printf "Hello\nWorld\n" }'

运行上面的 awk 命令,输出结果如下

Hello
World

后面那个 \n 是为了保证 终端( shell ) 的提示符在新的一行。

水平分隔符(制表符)\t

如果你想要两个或更多个字符串/字段之间有一定的间隔,一种方法是添加相当数量的 空格,比如我们想要 Hello 和 World 两个单词之间有四个空格,则可以直接使用四个空格分开

Hello    World

另一种更简单的方法,就是使用 水平分隔符(制表符)\t

Hello\tWorld

一般情况下,一个 \t 的间隙相当于 2 个或 4 个空格,这取决于配置

我们来看一个范例

[www.ddkk.com]$ awk 'BEGIN { printf "编号\t姓名\t部门\t年龄\n";printf "编号    姓名    部门    年龄\n";}'

运行上面的 awk 命令,输出结果如下

编号    姓名    部门    年龄
编号    姓名    部门    年龄

从上面的输出可以看出,在我这台电脑上,一个 \t 等于 4 个空格。

竖直(垂直)分隔符 \v

竖直(垂直)分隔符 \v 看起来有点和 水平分隔符(制表符)\t 类似,但是,前者的功能并不是在竖直方向拉出一定的空间,而是在 新行中空出上一行所占用的空间

简单的来说,就是制作一个 楼梯 效果,比如 "编号\v姓名\v部门\v年龄\n" 的作用,其实是

编号
   姓名
      部门
         年龄

[www.ddkk.com]$ awk 'BEGIN { printf "编号\v姓名\v部门\v年龄\n"}'

运行上面的 awk 命令,输出结果如下

编号
   姓名
      部门
         年龄

退格符 \b

退格符 \b 的作用,就是在之前的字符串基础上,往回退一个 字符。从某些方面说,就是删除一个前面的字符。

比如Hello\bWorld 输出的效果就是 HellWorld,也就是 \b 之前的那个 o 被删除了。

为了更逼真的演示 \b 的作用,请看下面这个范例

[www.ddkk.com]$ awk 'BEGIN { printf "Field 1Field 2Field 3Field 4\n" }'
[www.ddkk.com]$ 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) 的效果看起来类似,但它们实实在在的有点区别的。

1、 换行符(\n)是在新行继续输出;
2、 **回车符(\r)**则是删除当前行的所有已经输出,然后回到当前行的开始重新输出,从某些方面说,就是从在当前从头开始输出;
3、 回车换行(\r\n)的意思就是回到当前行的开始并且在新行输出,从某些方面说,其实就是换行

如果你比理解,请反复理解下面这个范例

[www.ddkk.com]$ awk 'BEGIN { printf "Field 1\nField 22\nField 333\nField 4444\n\n" }'
[www.ddkk.com]$ awk 'BEGIN { printf "Field 1\rField 22\rField 333\rField 4444\n\n" }'
[www.ddkk.com]$ 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.ddkk.com]$ awk 'BEGIN { printf "Sr No\fName\fSub\fMarks\n" }'
[www.ddkk.com]$ 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 格式化符。

需要注意的是

1、 如果传递的是数字,这个数字必须是ASCII所支持的,也就是说小于128
2、 如果传递的是字符串,那么只有第一个字符会被输出,其它的则直接省略;

[www.ddkk.com]$ awk 'BEGIN { printf "ASCII value 65 = character %c\n", 65 }'
[www.ddkk.com]$ awk 'BEGIN { printf "ASCII value 156 = character %c\n", 156 }'
[www.ddkk.com]$ awk 'BEGIN { printf "ASCII value ddkk = character %c\n", "ddkk" }'

运行上面的 awk 命令,输出结果如下

ASCII value 65 = character A
ASCII value 156 = character ?
ASCII value ddkk = character t

从输出结果中可以看出,如果传递的数字不能转换为 ASCII 字符,则直接输出一个 问号(?)

整数 %d 和 %i

%d 和 %i 格式化符的作用一样,把传递的参数转换为 整数 然后输出。

需要注意的是:

1、 如果传递的是浮点数(小数) ,那么只会输出整数部分,小数部分直接忽略;
2、 如果传递的是非整数,则直接输出0;
3、 如果传递的是字符串整数,则会转换为整数然后输出;

[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %d\n", 80 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %d\n", 80.66 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %d\n", "www.ddkk.com" }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %d\n", "80.66" }'

运行上面的 awk 命令,输出结果如下

价格 = 80
价格 = 80
价格 = 0
价格 = 80

科学计数法 %e 和 %E

%e 和 %E 的作用一样,把传递的参数先用 科学计数法 表示然后输出。它们的差别在于 %E 会把科学计数法表示中的所有字母都大写。

需要注意的是:

不管传递的是什么,都会先转换成科学计数法。如果传递的是字符串,首先会转换成 0 然后再转换为科学计数法

[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %e\n", 80 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %e\n", 80.6699999 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %e\n", "www.ddkk.com" }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %e\n", "80.66" }'

运行上面的 awk 命令,输出结果如下

价格 = 8.000000e+01
价格 = 8.067000e+01
价格 = 0.000000e+00
价格 = 8.066000e+01

%E 会把科学计数法中的所有字母都大写。

[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %E\n", 80 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %E\n", 80.6699999 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %E\n", "www.ddkk.com" }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %E\n", "80.66" }'

运行上面的 awk 命令,输出结果如下

价格 = 8.000000E+01
价格 = 8.067000E+01
价格 = 0.000000E+00
价格 = 8.066000E+01

浮点数格式化符 %f

浮点数格式化符 %f 会把传递的参数先转换为 浮点数(小数) ,然后输出。

需要注意的是:

1、 不管传递的是什么,都会先转换成浮点数如果传递的是字符串,首先会转换成0然后再转换为浮点数
2、 转换的时候,如果小数位数太多(默认多余6位)则会触发四舍五入

[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %f\n", 80 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %f\n", 80.6699999 }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %f\n", "www.ddkk.com" }'
[www.ddkk.com]$ awk 'BEGIN { printf "价格 = %f\n", "80.66" }'

运行上面的 awk 命令,输出结果如下

价格 = 80.000000
价格 = 80.670000
价格 = 0.000000
价格 = 80.660000

格式化符 %g 和 %G

%g 和 %G 的作用是一样的,会根据传递的参数选择 %e 或 %f 格式化符,具体选哪个,取决于转后后的结果谁更短(位数更少)。

而%G 和 %g 的区别,就是前者会把转换后的参数中的所有字母都大写。

1、 当给定的数字去掉首尾的空格后,整数部分总位数小于等于6时,使用的是%f和%F模式,且总共只保留6位数字,还会发生四舍五入;

格式化符 说明 范例 范例结果
%g 浮点数格式输出 printf "%g",000123.456789213 123.457
%G 浮点数格式输出且大写 E printf "%G",000123.456789213 123.456789

1、 当给定的数字去掉首尾的空格后,整数部分总位数大于6时,采用的是%e和%E模式,且会去掉首尾的空格,并保留6位有效小数;

格式化符 说明 范例 范例结果
%g 科学计数法输出 printf "%g",0005678123.456789213 5.67812e+06
%G 科学计数法输出且大写 E printf "%G",0005678123.456789213 123.456789

八进制 %o

%o 用于输出一个 无符号八进制数字,准确的说是将数据先转换为无符号八进制数据然后输出

[www.ddkk.com]$ awk 'BEGIN { printf "Octal representation of decimal number 10 = %o\n", 10}'

运行以上 awk 命令,输出结果如下

Octal representation of decimal number 10 = 12

%u

格式化符 %u 用于输出一个 无符号十进制数字,准确的说是将数据先转换为无符号十进制数据然后输出

[www.ddkk.com]$ awk 'BEGIN { printf "无符号 10 = %u\n", 10 }'

运行以上 awk 命令,输出结果如下

无符号 10 = 10

而对于小于 0 的数据,则会先转换为无符号十进制数字再输出,转换方式为先抛弃小数,然后求补码。s

[www.ddkk.com]$ awk 'BEGIN { printf "无符号 -11.332 = %u\n", -11.332 }'

运行以上 awk 命令,输出结果如下

无符号 -11.332 = 18446744073709551605

%s

格式化符 %s 用于输出一个字符串。准确的说是将数据先转换为字符串,然后输出

[www.ddkk.com]$ awk 'BEGIN { printf "年龄 = %s\n", 28.1119 }'

运行以上 awk 命令,输出结果如下

年龄 = 28.1119

当然了,%s 还可以原样输出传递的字符串,例如

[www.ddkk.com]$ awk 'BEGIN { printf "网站 = %s\n", "/"}'

运行以上 awk 命令,输出结果如下

网站 = /

%x and %X

格式化符 %x 和 %X 可以用来将一个 大于 0 的十进制 格式化为 十六进制

而%X 和 %x 的区别,就是 %X 格式化生成的十六进制是大写字母,而 %x 则是小写字母

[www.ddkk.com]$ awk 'BEGIN { printf "15 转换为十六进制为: = %x\n", 15}'

运行以上 awk 命令,输出结果如下

15 转换为十六进制为: = f

如果我们把 %x 改成 %X 则会输出大写的十六进制

[www.ddkk.com]$ awk 'BEGIN { printf "15 转换为十六进制为: = %X\n", 15}'

运行以上 awk 命令,输出结果如下

15 转换为十六进制为: = F

输出百分号 %%

已经学习了这么多的格式化符,我们知道,任何格式化符都是以 百分号(%) 开始的

那么,如果我们要输出 % 又要怎么做呢?

答案就是: 和 C 语言 一样,使用 两个百分号(%%) 来输出一个 %

[www.ddkk.com]$ awk 'BEGIN { printf "百分比 = %d%%\n", 80.66 }'

运行上面的 awk 命令,输出结果如下

百分比 = 80%

百分号(%) 的其它格式化符号

百分号(%) 还有一些其它非常有用的格式化组合,下面我们就来讲讲几个比较常见的

指定占用宽度

AWK使用 printf 输出时,默认是参数多长就输出多长,不会浪分一丁点空间。

但如果我们想要位某个参数指定固定的长度,则可以在 百分号(%) 后面,格式化字符之前添加指定的 长度

例如,对于 %d 如果我们要指定占用宽度为 10 ,则可以使用格式化符 %10d。

默认情况下,当输出是 右对齐的,且左边默认填充 空格(' ')

[www.ddkk.com]$ awk 'BEGIN { 
   num1 = 10; num2 = 20; printf "Num1 = %10d\nNum2 = %10d\n", num1, num2 
}'

运行上面的 awk 命令,输出结果如下

Num1 =         10
Num2 =         20

左对齐

在我们指定了宽度的前提下,AWK 有条默认的规则: 当输出的数字不足以填满宽度时,默认是右对齐的。例如下面的范例

[www.ddkk.com]$ awk 'BEGIN { num1 = 10;num2 = 12356; printf "Num1 = %7d\nNum2 = %7d\n", num1,num2 }' | cat -vte

输出结果如下

Num1 =      10$
Num2 =   12356$

上面的范例,有两个地方值得注意:

1、 输出默认时左对齐的,因此Num1=和Num2=垂直对齐且左对齐;
2、 当指定了宽度,默认时右对齐的10和123456就时右对齐;

如果我们想要输出的数字不足以填满宽度时仍然左对齐,则需要在百分号后类型之前面添加 负号(-)

对于上面的范例,就是将 %7d 改成 %-7d

[www.ddkk.com]$ awk 'BEGIN { num1 = 10;num2 = 12356; printf "Num1 = %-7d\nNum2 = %-7d\n", num1,num2 }' | cat -vte

运行上面的范例,输出结果如下

Num1 = 10     $
Num2 = 12356  $

前缀的 0

不知道你有没有留意上一条的数组结果。哈哈,去翻翻~~~~

在我们指定了宽度的前提下,AWK 还有另一条默认的规则: 当输出的数字不足以填满宽度时,默认右对齐的,且使用空格补足剩余部分。

例如下面的范例

[www.ddkk.com]$ 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.ddkk.com]$ 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.ddkk.com]$ awk 'BEGIN { 
   num1 = -10; num2 = 20; printf "Num1 = %+d\nNum2 = %+d\n", num1, num2 
}'

输出结果为

Num1 = -10
Num2 = +20

比如

[www.ddkk.com]$ awk 'BEGIN { 
   num1 = -10.234; num2 = 20.456; printf "Num1 = %+f\nNum2 = %+f\n", num1, num2 
}'

输出结果为

Num1 = -10.234000
Num2 = +20.456000

数字格式化

AWK的输出还支持对数字进行格式化,比如八进制、十六进制、小数点、千分位等。具体的规则如下

1、 如果给定的值不为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 而无视各种格式符

1、 对于任意数字,都是支持以下科学计数法输出;

格式化符 说明 范例 范例结果
%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

1、 对于任意数字,都支持以下浮点数格式化符;

格式化符 说明 范例 范例结果
%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

1、 对于任意数字,还支持以下浮点数格式化符;

%g 和 %G 是 %e、%E、%f、%F 的升级版。

1、 当给定的数字去掉首尾的空格后,整数部分总位数小于等于6时,使用的是%f和%F模式,且总共只保留6位数字,还会发生四舍五入;

格式化符 说明 范例 范例结果
%g 浮点数格式输出 printf "%g",000123.456789213 123.457
%G 浮点数格式输出且大写 E printf "%G",000123.456789213 123.456789
**2、** 当给定的数字去掉首尾的空格后,**整数**部分总位数大于6时,采用的是%e和%E模式,且会去掉首尾的空格,并保留6位有效小数;
格式化符 说明 范例 范例结果
%g 科学计数法输出 printf "%g",0005678123.456789213 5.67812e+06
%G 科学计数法输出且大写 E printf "%G",0005678123.456789213 123.456789