文章目录
一、简介
1.1、语法格式
awk [选项] '模式{动作}' file
-F 指定分隔符;
awk -F: '{print $1,$3}' /etc/passwd # 打印/etc/passwd中的用户和对应的uid;
-v 设置内部变量;
1.2、工作原理
awk是逐行处理的,逐行处理的意思就是说,当awk处理一个文本时,会一行一行进行处理,处理完当前行,再处理下一行,awk默认以"换行符"为标记,识别每一行,也就是说,awk跟我们人类一样,每次遇到"回车换行",就认为是当前行的结束,新的一行的开始,awk会按照用户指定的分割符去分割当前行,如果没有指定分割符,默认使用空格作为分隔符。
二、分隔符
2.1、输入分隔符 FS
英文原文为 field separator,默认以空白字符(即空格)为分隔符对每一行进行分割。
-F 选项指定输入分隔符
awk -F : '{print $1,$3}' /etc/passwd # 打印用户名和 UID,以空格进行分割;
或
# 设置内部变量,指定输入分隔符,但使用变量时,需要-v选项指定对应的变量,比如 -v FS=':',如下
awk -v FS=':' '{print $1,$3}' /etc/passwd # 打印用户名和UID,以空格进行分割;
2.2、输出分割符 OFS
英文原文为 output field separator,awk 将每行分割后,输出在屏幕上的时候,以什么字符作为分隔符,awk 默认的输出分割符也是空格。
awk -v FS=':' '{print $1,$3}' /etc/passwd # 用户名和 UID 默认以空格为分隔符进行分割;
awk -v FS=':' '{print $1 $3}' /etc/passwd # 用户名和 UID 紧挨着,不使用分隔符;
awk -v FS=':' '{print $1$3}' /etc/passwd # 用户名和 UID 紧挨着,不使用分隔符;
# 通过 -v OFS='分隔符' 设置内部变量,指定输出分隔符;
awk -v FS=':' -v OFS='_' '{print $1,$3}' /etc/passwd # 用户名和 UID 以下划线为分隔符进行分割;
awk -v FS=':' -v OFS='_' '{print $1"("$3")"}' /etc/passwd # 在 UID 上加个括号;
awk -v FS=':' -v OFS='+++' '{print $1,$3}' /etc/passwd
# 逗号,即分隔符,默认被设置为空格,这里设置为+++;
三、变量
3.1、内置变量
内置变量就是awk预定义好的、内置在awk内部的变量,而自定义变量就是用户定义的变量。
- awk 常用的内置变量及其作用如下:
FS:输入字段分隔符, 默认为空白字符
OFS:输出字段分隔符, 默认为空白字符
RS:输入记录分隔符(输入换行符), 指定输入时的换行符
ORS:输出记录分隔符(输出换行符),输出时用指定符号代替换行符
NF:number of Field,当前行的字段的个数(即当前行被分割成了几列),字段数量
NR:行号,当前处理的文本行的行号。
FNR:各文件分别计数的行号
FILENAME:当前文件名
ARGC:命令行参数的个数
ARGV:数组,保存的是命令行所给定的各参数
- 测试文件
[root@localhost tmp]# cat file1
abc#123#iuy#ddd
8ua#456#auv#ppp#7y7
[root@localhost tmp]# cat file2
abc 123 iuy ddd
8ua 456 auv ppp 7y7
-
FS & OFS
参考 “二、分隔符” ; -
NR 行号
[root@localhost tmp]# awk '{print NR,NF}' file2
1 4 # 打印每行的行号和列数;
2 5
[root@localhost tmp]# awk '{print NR,$0}' file2
1 abc 123 iuy ddd # 在行前添加行号;
2 8ua 456 auv ppp 7y7
在打印 $0 , $1 , $2 这些内置变量的时候,都有使用到"$" 符号,但在调用 NR , NF 时,就没有使用"$",如果不习惯,可能是你习惯了使用bash的语法去使用变量,在bash中,引用变量时,都会使用$符进行引用,但在 awk 中,只有在引用$0、$1等内置变量的值时才会用到"$",引用其他变量时,不管是内置变量,还是自定义变量,都不使用"$",而是直接使用变量名。
- FNR 对多文件的行号分别进行计数
当我们使用awk同时处理多个文件,并且使用NR显示行号的时候,效果如下:
[root@localhost tmp]# awk '{print NR,$0}' file1 file2
1 abc#123#iuy#ddd
2 8ua#456#auv#ppp#7y7
3 abc 123 iuy ddd
4 8ua 456 auv ppp 7y7
从返回结果可以看出,awk处理多个文件的时候,如果使用NR显示行号,那么,多个文件的所有行会按照顺序进行排序。
可是,如果我们想要分别显示两个文件的行号,该怎么办呢,这个时候就会用到内置变量FNR,效果如下:
[root@localhost tmp]# awk '{print FNR,$0}' file1 file2
1 abc#123#iuy#ddd
2 8ua#456#auv#ppp#7y7
1 abc 123 iuy ddd
2 8ua 456 auv ppp 7y7
通过比对上述两个示例,可以看出 FNR 的作用就是当 awk 处理多个文件时,对每个文件的行号分别进行计数。
- RS 输入行分隔符
默认的“行分隔符”即“回车换行”;要使用空格作为行分隔符,该怎么做?示例如下:
# 默认回车换行
[root@localhost tmp]# awk '{print NR,$0}' file2
1 abc 123 iuy ddd
2 8ua 456 auv ppp 7y7
# 设定为空格换行
[root@localhost tmp]# awk -v RS=" " '{print NR,$0}' file2
1 abc
2 123
3 iuy
4 ddd
8ua
5 456
6 auv
7 ppp
8 7y7
指定空格作为"行分隔符"时,每当遇到空格,awk就认为遇到的空格是换行符,于是awk就将文本换行了,而此时的"回车换行",对于awk来说并不是所谓的换行符,所以才会出现第4行的现象;
- ORS 输出行分隔符
默认的输出行分隔符为回车,若要使awk认为“+++”才是输出行分隔符该怎么做?示例如下:
[root@localhost tmp]# cat file2
abc 123 iuy ddd
8ua 456 auv ppp 7y7
[root@localhost tmp]# awk -v ORS="+++" '{print NR,$0}' file2
1 abc 123 iuy ddd+++2 8ua 456 auv ppp 7y7+++[root@localhost tmp]#
在没有指定输出行分隔符之前,awk跟人类的逻辑思维是一样一样的,当人类想要换行的时候,就会"另起一行"(回车换行),awk也是一样的,当它在输出文字的时候,如果想要换行,就会"另起一行"(回车换行), 可是,如果我们指定了"输出行分隔符"为"+++",那么,当awk在输出文字的时候,如果想要换行,就会"另起一行"(+++),所以,对于awk来说,它完成了"另起一行"的动作,只不过,它所认为的"另起一行"的动作就是输出"+++",而不再是原来的输出" 回车换行",所以,从人类看到的"表象上",awk并没有换行,那是因为我们还是以"回车换行"作为换行的标准,而awk已经变了,它认为,"+++"就是换行的标准。
我们把刚才学到的"输入换行符"和"输出换行符"同时使用,看看是什么效果,示例如下:
[root@localhost tmp]# cat file2
abc 123 iuy ddd
8ua 456 auv ppp 7y7
[root@localhost tmp]# awk -v RS=" " -v ORS="+++" '{print NR,$0}' file2
1 abc+++2 123+++3 iuy+++4 ddd
8ua+++5 456+++6 auv+++7 ppp+++8 7y7
+++[root@localhost tmp]#
- FILENAME 显示文件名
[root@localhost tmp]# awk '{print FILENAME,FNR,$0}' file1 file2
file1 1 abc#123#iuy#ddd
file1 2 8ua#456#auv#ppp#7y7
file2 1 abc 123 iuy ddd
file2 2 8ua 456 auv ppp 7y7
- ARGV
ARGV 内置变量表示的是一个数组,这个数组中保存的是命令行所给定的参数。这样解释还是很模糊,不容易理解,我们来看看示例。
[root@localhost tmp]# awk 'BEGIN{print "aaa"}' file1 file2
aaa
[root@localhost tmp]# awk 'BEGIN{print "aaa",ARGV[1]}' file1 file2
aaa file1
[root@localhost tmp]# awk 'BEGIN{print "aaa",ARGV[1],ARGV[2]}' file1 file2
aaa file1 file2
[root@localhost tmp]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1],ARGV[2]}' file1 file2
aaa awk file1 file2
从上可以看出:
ARGV[0]=awk
ARGV[1]=file1
ARGV[2]=file2
第一个参数居然是awk命令本身,而'pattern{action}'
并不被看做是参数;
- ARGC
ARGC 内置变量表示参数的数量,也可以理解为 ARGV 数组的长度。示例如下:
[root@localhost tmp]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1],ARGV[2],ARGC}' file1 file2
aaa awk file1 file2 3
3.2、自定义变量
自定义变量,顾名思义,就是用户定义的变量,有两种方法可以自定义变量。
- 方法一:-v varname=value 变量名区分字符大小写。
[root@localhost tmp]# awk -v myVar="testVar" 'BEGIN{print myVar}'
testVar
这种方式,与设置内置变量的值的方法是一样的。
- 方法二:直接在program中直接定义即可,但是注意,变量定义与动作之间需要用分号";"隔开。
[root@localhost tmp]# awk 'BEGIN{myvar="ttt";print myvar}'
ttt
当然,我们也可以一次性定义多个变量
[root@localhost tmp]# awk 'BEGIN{ myvar1="111"; myvar2="222";print myvar1,myvar2 }'
111 222
第一种方法虽然看上去比较麻烦,但是这种方法也有自己的优势
当我们需要在awk中引用shell中的变量的时候,则可以通过方法一间接的引用。举例如下
[root@localhost tmp]# abc=6666666
[root@localhost tmp]# awk -v myvar=$abc 'BEGIN{print myvar}'
6666666
四、格式化
在使用 awk 时,通常使用 print 对文本进行输出,但是动作 print 只能实现简单的文本输出功能,并不能对文本格式进行改变,如果想要改变文本的格式,则需要 awk 中的另一个动作 printf。
4.1、printf 命令详解
printf 命令的主要作用就是按照我们指定的格式输出文本。
[root@localhost tmp]# echo testString
testString
[root@localhost tmp]# printf testString
testString[root@localhost tmp]#
从上述示例中可以看出,在输出文本时,echo命令会对输出的文本进行换行,而printf命令则不会对输出的文本进行换行,那么,如果我们想要利用printf将输出的文本换行,应该怎么做呢?没错,聪明如你一定想到了,使用转义符\n ,示例如下:
[root@localhost tmp]# printf "testString\n"
testString
看到这里你可能会说,这样多麻烦,还不如直接使用echo命令比较方便,没错,如果只是就输出功能而言,它们并没有什么不同,但是,刚才已经强调过,printf 的优势在于格式化输出文本,那么,我们就来通过一个小例子,来见识一下 printf 的格式化输出文本的能力。
假设,我们有一串文本需要输出,如下:
[root@localhost tmp]# echo "abc def ghi jkl mno"
abc def ghi jkl mno
现在我们有一个小需求,就是将上述文本按照空格分段,每段单独输出在一行里面。
当然,有很多方法可以实现上述要求,假设我们并不会使用其他命令,也不是特别会写脚本,那么我们可能会使用一些笨办法进行输出,比如:
[root@localhost tmp]# echo -e "abc \ndef \nghi \njkl \nmno"
abc
def
ghi
jkl
mno
[root@localhost tmp]# printf "abc \ndef \nghi \njkl \nmno \n"
abc
def
ghi
jkl
mno
[root@localhost tmp]# printf "%s\n" abc def ghi jkl mno
abc
def
ghi
jkl
mno
上述示例中,命令1与命令2在输出文本时,都使用了转义字符\n将文本换行了,但是,在写命令1与命令2的时候,我的内心是崩溃的,因为,上述示例还算简单,我只是将5段文本分成5行输出即可,但是,如果是一个100段的文本呢?难道我们要在每一段中都添加一个转义字符"\n",那我的手不就打残了,而命令3则不同,我只是通过printf指定了一个固定的"格式",后面的每一段文本都按照指定的格式进行了换行,即使有10000段文本需要换行,我也不用担心手残的问题了,从这个小例子中,我们就能体会到printf的格式化能力,上图中命令3中使用到的"格式"会在后面进行详细的描述,不要着急。
- 语法格式:
聪明如你一定想到了,上述语法中的每一个"文本"都会被当做参数项传入printf命令,而每个被传入的参数都会按照指定的"格式"被"格式化"。
没错,命令3中的"%s\n"即为我们指定的"格式",而后面的每一段字符串,都被当做参数传入到了printf命令中,并按照我们指定的格式进行了格式化。那么,我们现在来详细的解释一下上图中的"%s\n"是什么意思。
我们先说说"%s"是什么意思,"%s"是一个"替身演员",我们使用"%s"代替传入的参数,也就是说, “%s"代替了命令3中的abc,代替了def,代替了ghi,代替了每一个传入的参数,在我们指定的"格式"中,它代表了每一个传入的参数,所以,如果我们指定的格式为”%s\n",当abc被当做参数传入printf命令时,printf就会把"%s\n"中的%s替换成abc,于是,abc就变成了我们指定的格式"abc\n",最终printf输出的就是格式化后的"abc\n",以此类推,每一段文本都被当做一个参数传入printf命令,然后按照指定的格式输出了。
而"替身演员"只是我给"%s"起的一个外号,它的真名叫"格式替换符",而printf中,“格式替换符"不只有”%s"一种,"%s"代替了每一个传入的参数,并将他们转化成了"字符串类型",我们再来认识一个新的替身演员,"%f" ,"%f"也代替了每一个传入的参数,与"%s"不同的是,"%f"会将每一个传入的参数转换成"浮点类型",我们来看一个小例子。
[root@localhost tmp]# printf "%s\n" 1 9 18 666
1
9
18
666
[root@localhost tmp]# printf "%f\n" 1 9 18 666
1.000000
9.000000
18.000000
666.000000
上例中,我们分别使用了"%s"替换符和"%f"替换符格式化了相同的内容,但是格式化后的结果却不同。
"%f"自动将传入的数字添加了小数点,将传入的数字参数替换成了浮点数。
聪明如你一定想到了,我们可以根据传入参数的不同,使用不同的"格式替换符"去替换。
那么,还有哪些格式替换符呢?常用的格式替换符总结如下。
%s 字符串
%f 浮点格式(也就是我们概念中的float或者double)
%b 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义。
%c ASCII字符。显示相对应参数的第一个字符
%d, %i 十进制整数
%o 不带正负号的八进制值
%u 不带正负号的十进制值
%x 不带正负号的十六进制值,使用a至f表示10至15
%X 不带正负号的十六进制值,使用A至F表示10至15
%% 表示"%"本身
说完了"格式替换符",再来说说"转义字符",刚才的示例中,我们只用到了"\n"这个转义符,还有很多其他的转义符,printf中的转义字符与其他程序中的转义字符没有什么不同,此处我们只是总结出来,方便大家使用。printf常用的转义符如下。
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\ 一个字面上的反斜杠字符,即""本身。
\ddd 表示1到3位数八进制值的字符,仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符
现在,我们已经会基本的使用printf命令了,但是还有很多东西没有细聊,在"深入"之前,我们先"浅出"一下,把刚才的知识应用一遍,动手做一些小例子,为后面的知识点打下坚实的基础。
假设,我想为每个传入的参数添加一对"括号",并且括号内侧需要有空格,那么我们可以使用如下命令。
[root@localhost tmp]# printf "( %s )" 1 9 18 666;echo ""
( 1 )( 9 )( 18 )( 666 )
假设,我们想要将每个传入的参数使用"制表符"隔开,那么,可以使用如下命令。
[root@localhost tmp]# printf "%s\t" 1 9 18 666;echo ""
1 9 18 666
好了,我知道你的动手能力一定比我强,所以,你自己玩吧,之前的知识点随便试,我就不再瞎BB了。
现在,我们继续聊printf。
刚才的举例中,我们使用到的"格式"其实很简单,每个"格式"中都只用到了一个"格式替换符",现在,我们扩展一下,在格式中设置多个"格式替换符"试试,示例如下
[root@localhost tmp]# printf "%s\n" a b c d e f
a
b
c
d
e
f
[root@localhost tmp]# printf "%s %s\n" a b c d e f
a b
c d
e f
看完上图,聪明如你一定想到了,我们所指定的"格式"中所包含的"格式替换符"的数量,就代表每次格式化的参数的数量,每个"格式替换符"与参数都是一一对应的,上图中,指定的"格式"中包含两个"格式替换符",那么printf每次进行"格式化"操作时,就会传入两个参数,然后前一个参数对应第一个替换符,后一个参数对应第二个替换符,当本次格式化操作完成以后,再传入下一波参数,示意图如下
那么,我们再验证一遍上述理论,把之前的格式改为如下图中的模样
[root@localhost tmp]# printf "%s %s %s\n" a b c d e f
a b c
d e f
按照之前的理论,因为"格式"中包含3个"格式替换符",所以每轮格式化都可以一次性格式化3个参数,于是,第一次格式化操作将"%s %s %s\n"替换成了"a b c\n",第二次格式化操作将"%s %s %s\n"替换成了"d e f\n",格式化后的输出如上所示。
好了,我们再看一个例子,跟上例其实没有区别,只是传入的数据更贴合实际,所以格式化后的数据可读性更高了。
[root@localhost tmp]# printf "%s %s %s\n" 姓名 性别 年龄 尼玛 男 20 尼美 女 18
姓名 性别 年龄
尼玛 男 20
尼美 女 18
如果你跟我一样,有所谓的强迫症,那么你对上图中的格式化效果肯定不满意,上图中,被格式化以后的数据虽然可读性更高了,但是并没有完全对齐,“年龄"字段对应的数字都快跑到"性别"字段里面了,这怎么能忍?其实我就是说说,真忍也就忍了。不过咱们也可以不这么委屈自己,因为printf有能力"满足"这样的"需求”。(嗯~~~说这句话的时候我想歪了)
我们看看,printf是怎样拯救强迫症患者的,示例如下。
[root@localhost tmp]# printf "%7s %5s %4s\n" 姓名 性别 年龄 尼玛 男 20 尼美 女 18
姓名 性别 年龄
尼玛 男 20
尼美 女 18
我们只是在原来的"格式替换符"中间加入了特定的数字,貌似显示效果就比刚才好了一点,起码年龄字段对应数字与"年龄"两个字对齐了。那么这些数字是什么意思呢?上图中第一个"%7s"中间的7表示当前替换符对应的输出宽度为7个字符宽,如果对应的输出不足7个字符,则用空格补全,如果输出的长度超过7个字符,超出的部分也会显示。同理"%5s"表示当前替换符对应的输出宽度为5个字符的宽度。而这些数字,我们可以将其称之为"修饰符",修饰符会对相应的"替换符"进行修饰。
神马?看右对齐不顺眼,想要左对齐?好吧,满足你!看下图。
[root@localhost tmp]# printf "%-7s %-5s %-4s\n" 姓名 性别 年龄 尼玛 男 20 尼美 女 18
姓名 性别 年龄
尼玛 男 20
尼美 女 18
与之前的"格式"相比,只是在原来的修饰符前面加入了"-","-“表示左对齐,默认不加”-“时表示右对齐,其实”-"也是修饰符。
除了数字和"-",还有另一种修饰符,就是"+"
"+“可不代表右对齐,因为我们前面已经说过,不加”-“修饰符的时候,默认就是右对齐。那么”+"修饰符表示什么意思呢?看下图你就会明白。
[root@localhost tmp]# printf "灵宠名称 体温\n";printf "%10s %5d \n" 烈火兽 180 冰晶兽 -70
灵宠名称 体温
烈火兽 180
冰晶兽 -70
[root@localhost tmp]# printf "灵宠名称 体温\n";printf "%10s %+5d \n" 烈火兽 180 冰晶兽 -70
灵宠名称 体温
烈火兽 +180
冰晶兽 -70
如上图所示,在默认没有添加"+“作为修饰符时,烈火兽的体温输出为180,而使用了”+“修饰符以后,烈火兽的体温输出为+180,看到这里你应该明白了,当替换符对应的参数为数字时,可以使用”+“作为修饰符,输出"正数"前面的"正号”。
现在我们已经学会了3种修饰符
“数字”
“+”
“-”
其实数字修饰符还有另外的一些特殊用法,如下图所示。
[root@localhost tmp]# printf "灵宠名称 体温\n";printf "%-10s %-12f \n" 烈火兽 180.5289 冰晶兽 -70.666
灵宠名称 体温
烈火兽 180.528900
冰晶兽 -70.666000
[root@localhost tmp]# printf "灵宠名称 体温\n";printf "%-10s %-12.3f \n" 烈火兽 180.5289 冰晶兽 -70.666
灵宠名称 体温
烈火兽 180.529
冰晶兽 -70.666
上图中,第一条命令的数字修饰符为12,表示对应的替换符"%f"的输出宽度为12个字符,第二条命令的数字修饰符为12.3 ,表示对应的替换符"%f"的输出宽度为12个字符,并且小数点的精度为3。
没错,当替换符为"%f"时,如果数字修饰符带有小数点,则数字修饰符小数点后的数字表示对应小数精度。
而当格式替换符为"%d"时,如果数字修饰符带有小数点,则数字修饰符小数点后的数字表示整数的长度,长度不够时,高位用0补全,如下图所示。
[root@localhost tmp]# printf "灵宠名称 体温\n";printf "%-10s %-12d \n" 烈火兽 180 冰晶兽 -70
灵宠名称 体温
烈火兽 180
冰晶兽 -70
[root@localhost tmp]# printf "灵宠名称 体温\n";printf "%-10s %-12.5d \n" 烈火兽 180 冰晶兽 -70
灵宠名称 体温
烈火兽 00180
冰晶兽 -00070
好了,printf命令的用法就总结到这里!
4.2、格式化输出
利用awk中的printf动作,即可对文本进行格式化输出,printf动作的用法与printf命令的用法非常相似,只是有略微的不同而已,不过,我们还是从最简单的示例开始看起,首先对比一下print动作与printf动作的区别,示例如下:
[root@localhost tmp]# cat file2
abc 123 iuy ddd
8ua 456 auv ppp 7y7
[root@localhost tmp]# awk '{print $1}' file2
abc
8ua
[root@localhost tmp]# awk '{printf $1}' file2
abc8ua[root@localhost tmp]#
没错,printf动作与printf命令一样,都不会输出换行符,默认会将文本输出在一行里面。
聪明如你一定想到了,既然printf动作的用法与printf命令一样,那么,printf动作有没有printf命令中所谓的"格式替换符"呢?
必须有啊,"格式替换符"是什么我们就不再赘述了,因为在printf命令详解中已经详细的解释过它,那么我们来使用"格式替换符"来指定一下$1的格式,示例如下:
[root@localhost tmp]# awk '{printf "%s\n", $1}' file2
abc
8ua
如果只看上例,你肯定会认为,这就是printf命令的用法,只是printf动作与printf命令在语法上唯一的不同点就是,在使用printf动作时,指定的"格式"与列($1)之间需要用"逗号"隔开,而使用printf命令时,指定的格式与传入的文本不需要使用"逗号"隔开,如下图所示:
[root@localhost tmp]# awk '{printf "%s\n", $1}' file2
abc
8ua
[root@localhost tmp]# printf "%s\n" string
string
其实,它们还有一些其他的不同之处,我们在使用printf命令时,当指定的格式中只有一个"格式替换符",但是传入了多个参数时,那么这多个参数可以重复的使用这一个格式替换符,示例如下:
[root@localhost tmp]# printf "%s\n" 1 2 3 4 5
1
2
3
4
5
但是在awk中,我们则不能这样使用,在awk中,格式替换符的数量必须与传入的参数的数量相同,换句话说,格式替换符必须与需要格式化的参数一一对应,示例如下:
[root@localhost tmp]# awk 'BEGIN{printf "%s\n", 1,2,3,4,5 }'
1
[root@localhost tmp]# awk 'BEGIN{printf "%s\n%s\n%s\n%s\n%s\n", 1,2,3,4,5 }'
1
2
3
4
5
好了,这就是awk中printf动作在使用时的一些注意点。
我们来总结一下,在awk中使用printf动作时,需要注意以下3点。
1)使用printf动作输出的文本不会换行,如果需要换行,可以在对应的"格式替换符"后加入"\n"进行转义。
2)使用printf动作时,“指定的格式” 与 “被格式化的文本” 之间,需要用"逗号"隔开。
3)使用printf动作时,"格式"中的"格式替换符"必须与 “被格式化的文本” 一一对应。
好了,我们来看一些小示例,练练手。
我们可以利用格式替换符对文本中的每一列进行格式化,示例如下:
[root@localhost tmp]# awk '{printf "第一列: %s 第二列: %s\n" ,$1,$2}' file2
第一列: abc 第二列: 123
第一列: 8ua 第二列: 456
我们可以利用awk的内置变量FS,指定输入字段分隔符,然后再利用printf动作,进行格式化,示例如下:
[root@localhost tmp]# cat file1
abc#123#iuy#ddd
8ua#456#auv#ppp#7y7
[root@localhost tmp]# awk -v FS="#" '{printf "第一列:%s\t 第二列:%s\n", $1,$2}' file1
第一列:abc 第二列:123
第一列:8ua 第二列:456
上例完美的体现了awk的格式化能力,因为awk本身负责文本切割,printf动作负责格式化文本,双剑合璧了。
继续扩展一下,可以利用awk的begin模式,结合printf动作,输出一个像样的表格,下图中用到的"修饰符"此处不再赘述,如果不明白,参考printf命令详解。
[root@localhost tmp]# awk -v FS=":" 'BEGIN{printf "%s\t %s\n" ,"用户名称","用户ID"}{printf "%-10s\t %s\n",$1,$3}' /etc/passwd
用户名称 用户ID
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
operator 11
games 12
ftp 14
nobody 99
systemd-network 192
dbus 81
polkitd 999
sshd 74
postfix 89
chrony 998
其实话说回来,只要能够灵活的使用printf命令,再结合printf动作使用时的3个注意点,即可快速灵活的掌控它,好了,关于awk的格式化能力,就暂时总结到这里,希望这篇文章能够对你有所帮助。
五、模式
5.1、特殊模式
AWK 包含两种特殊的模式:BEGIN 和 END;
BEGIN 模式指定了处理文本之前需要执行的操作;
END 模式指定了处理完所有行之后所需要执行的操作;
不理解的话,参考 awk 工作原理;
- 测试文件
[root@localhost tmp]# cat file2
abc 123 iuy ddd
8ua 456 auv ppp 7y7
- BEGIN
[root@localhost tmp]# awk 'BEGIN{print "aaa","bbb"}' file2
aaa bbb
上述写法表示,在开始处理file2文件中的文本之前,先执行打印动作,输出的内容为"aaa",“bbb”,也就是说,上述示例中,虽然指定了 fiel2 文件作为输入源,但是在开始处理file2文本之前,需要先执行BEGIN模式指定的"打印"操作,既然还没有开始逐行处理file2文件中的文本,那么是不是根本就不需要指定file2文件呢,我们来试试。
[root@localhost tmp]# awk 'BEGIN{print "aaa","bbb"}'
aaa bbb
经实验发现,在没有给定任何输入来源时,awk就直接输出信息了,因为,BEGIN模式表示,在处理指定的文本之前,需要先执行BEGIN模式中指定的动作,而上述示例没有给定任何输入源,但是awk还是会先执行BEGIN模式指定的"打印"动作,打印完成后,发现并没有文本可以处理,于是就只完成了"打印 aaa bbb"的操作。
这个时候,如果我们想要awk先执行BEGIN模式指定的动作,再根据执我们自定义的动作去操作文本,该怎么办呢?示例如下
[root@localhost tmp]# awk 'BEGIN{print "aaa","bbb"}{print $1,$2}' file2
aaa bbb
abc 123
8ua 456
如上所示,首行表示BEGIN模式指定的动作,当BEGIN模式对应的动作完成后,再逐行处理文本,即打印 file2 文件中的第一列与第二列;以此类推,END模式的作用就一目了然了,如下。
[root@localhost tmp]# awk '{print $1,$2}END{print "ccc","ddd"}' file2
abc 123
8ua 456
ccc ddd
如上所示,尾行即END模式执行的动作,就是在处理完所有指定的文本之后,需要执行的动作;
那么结合BEGIN模式和END模式一起使用,示例如下
[root@localhost tmp]# awk 'BEGIN{print "aaa","bbb"}{print $1,$2}END{print "ccc","ddd"}' file2
aaa bbb
abc 123
8ua 456
ccc ddd
上述返回的结果有没有很像一张"报表",有"表头" 、“表内容”、 “表尾”,awk对文本的格式化能力你体会到了吗?