【5】三剑客:awk

一、简介


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对文本的格式化能力你体会到了吗?

5.2、普通模式

六、动作


七、数组


八、内置函数


九、拾遗之”三元运算”与”打印奇偶行”


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值