Linux有关文本处理的三剑客(grep、sed、awk)

文本三剑客

  • 文本处理三剑客
    • grep命令:文本过滤工具(文本内容-只能匹配显示不能修改)
    • sed命令:文本行编辑器(文本内容-可匹配可修改)
    • awk命令:文本分析工具(文本内容-只能匹配不能修改)
        • awk命令行方式基本用法:
        • awk变量:
        • 操作符
        • awk高级用法:即awk控制语句
        • awk数组
        • awk函数
        • awk脚本

文本处理三剑客

grep命令:文本过滤工具(文本内容-只能匹配显示不能修改)

指令用于查找内容包含指定的范本样式的文件,如果发现某文件的内容符合所指定的范本样式,预设grep指令会把含有范本样式的那一列显示出来。若不指定任何文件名称,或是所给予的文件名为"-",则grep指令会从标准输入设备读取数据。
格式:
#grep [OPTION]… PATTERN [FILE]…
grep常用参数:

[OPTIONS]:
  -B num  : 除了显示符合样式的那一行之外,并显示该行之前的num行内容。before
  -A num :除了显示符合样式的那一行之外,并显示该行之前的num行内容。after
  -C num:除了显示符合样式的那一行之外,并显示该行之前后num行内容。
  -c :计算符合样式的列数
  -d 动作: 当指定要查找的是目录而非文件时,必须使用这项参数,否则grep指令将回报信息并停止动作。
  -e :实现多个选项间的逻辑or关系.
  -n :在显示符合样式的那一行之前,标出该行的列数编号,即显示行号;
  -E :使用扩展的正则表达式  \egrep
  -i :忽略字符大小写的差别
  -o :只显示匹配的字符串部分
  -v :显示不包含匹配文本的所有行
  -V :显示版本信息
  -s :不显示错误信息。
  -w:强制'pattern'匹配部分只匹配整个单词。

练习:

///取出ifconfig命令中的所有ipv4地址。
[root@localhost Packages]# ifconfig|grep "inet "
        inet 192.168.10.132  netmask 255.255.255.0  broadcast 192.168.10.255
        inet 192.168.1.130  netmask 255.255.255.0  broadcast 192.168.1.255
        inet 127.0.0.1  netmask 255.0.0.0
[root@localhost Packages]# ifconfig|grep "inet "|grep  -Eo "[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}"   
192.168.10.132
255.255.255.0
192.168.10.255
192.168.1.130
255.255.255.0
192.168.1.255
127.0.0.1
255.0.0.0

sed命令:文本行编辑器(文本内容-可匹配可修改)

了解:

  • sed命令是非交互式的行编辑器。
  • sed即可以对文本内容进行过滤,也可以对文本内容进行修改(默认情况下是不进行修改的,除非使用-i参数,或重定向保存结果)。默认情况下,所有的输出行都被打印到屏幕上。
  • 工作流程:sed编辑器逐行处理文件(或输入),即一次只处理一行内容,并将结果发送到屏幕。具体过程如下:首先sed把当前正在处理的行保存在一个临时缓冲区中(也称为模式空间),然后处理临时缓冲区中的行,处理完成后把该行发送到屏幕上。sed每处理完一行就将其从临时缓冲区删除,然后将下一行读入,进行处理和显示。处理完输入文件的最后一行后,sed便结束运行。sed把每一行都存在临时缓冲区中,对这个副本进行编辑,所以不会修改原文件。
  • sed是一种语言,awk是一种语言,bash也是一种语言,即是一门语言,那么就会有自己的使用方式。
  • sed是对文本一行一行进行处理的,若不加地址定界,或地址定界匹配不带,则默认会一行一行的匹配处理文本内容,直到结束。

用法:#sed [option] … ‘script’ inputfile…

[option]:
-n:不输出模式空间内容到屏幕,即不自动打印。
-e:多点编辑。
-f:/PATH/SCRIPT_FILE:从指定文件中读取编辑脚本。
-r:支持使用扩展正则表达式。
-i.bak:备份原文件后,对原文件进行编辑修改。
 	注:i.bak是因为sed命令使用-i参数时不会询问,怕改错,有一定的危险性,因此-i.bak是在修改前先备份原文件,然后再修改该文件,一定程度上减少了出错风险。

inputfile:处理的文件
script:‘地址定界和编辑命令’;script是sed语言自身的语言脚本。

地址定界:
(1)不给地址:对全文进行处理。
(2)单地址:
		#:指定的行,$:最后一行
		/pattern/:被此处模式所能匹配到的每一行。支持正则表达式。
(3)地址范围:
		#,#:如1,3:显示1到3行。
		#,+#:如1,+6:显示1到7行(第一行再加6行)。
		/pat1/,/pat2/:如/^ar1/,/^ar2/:匹配显示以ar1开头行~以ar2开头的行。支持正则表达式
		#,/pat1/:如1,/^ar1:匹配显示从第一行至以"ar1"开头的行。
(4)~:步进
		1~2:奇数行
		2~2:偶数行

编辑命令:
注意—>多个编辑命令一起使用时,需要使用";"分隔。

编辑命令
d:删除模式空间匹配的行,并立即启用下一轮循环。即删除匹配的行。
p:打印当前模式空间内容,并追加到默认输出之后。
a\text:在指定行后面追加文件“text”。支持使用“\n”实现多行追加。
i\text:在指定行前面插入文本“text”。
c\text:替换行尾单行或多行文本。
w /path/somefile:保存模式匹配的行至指定文件。
r /path/somefile:读取指定文件的文本至模式空间中,匹配到行后。
=:为模式空间中的行打印行号。
!:模式空间中匹配行取反处理
s///:查找替换,支持使用其它分隔符,如s@@@,s###等。
	替换标记:
		s///:若一行内有多个重复内容,只替换第一个重复内容。
		s///g:行内全局替换。
		s///p:显示替换成功的行。
		s///w /path/to/somefile:将替换成功的行保存至文件中。

高级编辑命令:“再添加一个保持空间,可以理解为模式空间的行缓存”

高级编辑命令:
P:打印模式空间开端至\n内容,并追加到默认输出之前。
h:把模式空间中的内容覆盖至保持空间中。
H:把模式空间中的内容追加至保持空间中。
g:从保持空间取出数据覆盖至模式空间。
G:从保持空间取出内容追加至模式空间。
n:读取匹配到的行的下一行追加至模式空间。
N:读取匹配到的行的下一行追加至模式空间。
d:删除模式空间中的行。
D:	如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本,并不会读取新的输入行,而使用合成的模式空间重新启动。
	如果模式空间不包含换行符,则会像发出d命令那样启动正常的新循环。
x:把模式空间中的内容与保持空间中的内容进行互换。

简单练习:

了解sed的-n参数与p编辑命令的意义
[root@localhost ~]# seq 1 5
1
2
3
4
5
[root@localhost ~]# seq 1 5|sed '2p'
1
2
2
3
4
5
[root@localhost ~]# seq 1 5|sed -n '2p'
2

///显示#seq 1 5 的奇数行和偶数行
[root@localhost ~]# seq 1 5|sed -n '1~2p'
1
3
5
[root@localhost ~]# seq 1 5|sed -n '2~2p'
2
4

//地址定界的使用
[root@localhost ~]# cat a.txt 
cctv1.wfwofwof
aodiwa
erge
cctv1.mogm232
awdnaid
[root@localhost ~]# cat a.txt |sed -n '/^cctv1/p'
cctv1.wfwofwof
cctv1.mogm232
[root@localhost ~]# cat a.txt |sed -n '/^cctv1/,/^cctv1/p'
cctv1.wfwofwof
aodiwa
erge
cctv1.mogm232
[root@localhost ~]# cat a.txt |sed -n '2,/^cctv1/p'
aodiwa
erge
cctv1.mogm232

///地址定界+编辑命令的使用
[root@localhost ~]# seq 1 5
1
2
3
4
5
[root@localhost ~]# seq 1 5|sed  '1,2d'
3
4
5

[root@localhost ~]# seq 1 5|sed  '1,2a\  = = ='
1
  = = =
2
  = = =
3
4
5
[root@localhost ~]# seq 1 5|sed  '1,2i\  = = ='
  = = =
1
  = = =
2
3
4
5
[root@localhost ~]# seq 1 5|sed  '2c\  = = ='
1
  = = =
3
4
5
[root@localhost ~]# seq 1 5|sed  '1,2c\  = = ='
  = = =
3
4
5

[root@localhost ~]# seq 1 5|sed  -n '1,3w /root/ex.txt'
[root@localhost ~]# cat ex.txt 
1
2
3
[root@localhost ~]# cat ex.txt 
=======
[root@localhost ~]# seq 1 3|sed   '1,2r /root/ex.txt'
1
=======
2
=======
3

[root@localhost ~]# cat ex.txt 
=======
wwafr
212
[root@localhost ~]# cat ex.txt|sed   '='
1
=======
2
wwafr
3
212

[root@localhost ~]# seq 1 3
1
2
3
[root@localhost ~]# seq 1 3 |sed -n '1,2!p'
3

复杂练习:

'1.在/etc/sysconfig/grub文件中的quiet和结尾"冒号"之间加上net.ifnames=0'
[root@localhost ~]# cat /etc/sysconfig/grub 
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
[root@localhost ~]# sed -nr '/GRUB_CMDLINE_LINUX/s/(")$/ net.ifnames=0\1/p'  /etc/sysconfig/grub 
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet net.ifnames=0"
[root@localhost ~]# sed -nr '/GRUB_CMDLINE_LINUX/s/(quiet)"$/\1 net.ifname=0"/p'  /etc/sysconfig/grub 
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet net.ifname=0"

'2.取出ifconfig命令中的指定地址'
[root@localhost ~]# ifconfig|sed -n '/inet /p'
        inet 192.168.10.132  netmask 255.255.255.0  broadcast 192.168.10.255
        inet 192.168.1.130  netmask 255.255.255.0  broadcast 192.168.1.255
        inet 127.0.0.1  netmask 255.0.0.0
[root@localhost ~]# ifconfig|sed -n '/inet /p'|sed -nr 's/inet (.*)netmask (.*)broadcast (.*)/\1/p'
        192.168.10.132  
        192.168.1.130  
[root@localhost ~]# ifconfig|sed -n '/inet /p'|sed -nr 's/inet (.*)netmask (.*)broadcast (.*)/\3/p'
        192.168.10.255
        192.168.1.255

'3.在aa.txt文本中以a2为首的行后添加"path=\/root\/example"字段。'
[root@localhost ~]# cat aa.txt 
a1
a2
a3
[root@localhost ~]# sed -nr  '/^a2/s/(.*)/\1 path=\/root\/example /p' /root/aa.txt 
a2 path=/root/example 
# sed -nr  '/^a2/s/a2/& path=\/root\/example /p' /root/aa.txt     //"&"代表前面匹配的字符串
a2 path=/root/example 

'4.显示aa.txt文本中的空行,及其行号。'
[root@localhost ~]# cat aa.txt 
a1

a2
a3
[root@localhost ~]# sed -n -e '/^$/p' -e '/^$/=' /root/aa.txt 

2

'5.将a.conf文本中的NAME=80的#去掉,将行的#去掉。'
[root@localhost ~]# cat a.conf 
#Name=80
#AIHI
#NIIEFSJ
#
#SRGTFHTFH
#SRHTTRT
#
[root@localhost ~]#sed -nr '/^#Name=/s/#//p'  /root/a.conf
Name=80
[root@localhost ~]#sed -nr '/^#
<Vir *:80>
SRGTFHTFH
SRHTTRT
</Vir>
[root@localhost ~]# sed -r -i.bak -e '/^#Name=/s/#//'  -e '/^#
[root@localhost ~]# ls
a.conf   a.conf.bak
[root@localhost ~]# cat a.conf
Name=80
#AIHI
#NIIEFSJ
<Vir *:80>
SRGTFHTFH
SRHTTRT
</Vir>
///错误的例子:该修改会将显示的输出结果作为最终判别
[root@localhost ~]# sed -nr -i.bak1 -e '/^#Name=/s/#//p'  -e '/^#
[root@localhost ~]# ls
a.conf  a.conf.bak1
[root@localhost ~]# cat a.conf
Name=80
<Vir *:80>
SRGTFHTFH
SRHTTRT
</Vir>

'6.将ex.txt文本中第一行的第一个s替换为S,将第二行的所有s替换为S'
[root@localhost ~]# cat ex.txt 
WWW s WWWsss
WWW s WWWsss
[root@localhost ~]# sed -nr -e '1s/s/S/p' -e '2s/s/S/gp' /root/ex.txt 
WWW S WWWsss
WWW S WWWSSS

'7.取下列路径的目录名和基名;'
root@localhost ~]# echo "/etc/sysconfig/network/"|sed -nr 's/(.*\/)([^/]+\/?$)/\1/p'
/etc/sysconfig/
root@localhost ~]# echo "/etc/sysconfig/network/"|sed -nr 's/(.*\/)([^/]+\/?$)/\2/p'
network/
[root@localhost ~]# echo "/etc/sysconfig/network"|sed -nr 's/(.*\/)([^/]+\/?$)/\1/p'
/etc/sysconfig/
[root@localhost ~]# echo "/etc/sysconfig/network"|sed -nr 's/(.*\/)([^/]+\/?$)/\2/p'
network

'8.将奇数行和偶数行合并显示;'
[root@localhost ~]# seq 1 10|sed -n 'N;s/\n/ /p'
1 2
3 4
5 6
7 8
9 10
[root@localhost ~]# seq 1 10|xargs -n2
1 2
3 4
5 6
7 8
9 10

'9.删除文本内容,只保留最后一行。'
#seq 1 5 |sed 'N;D'

'10.统计/dev/sr0/Packages目录下的rpm包的架构类型(倒数第二列),并统计各类型有多少rpm包。'
[root@localhost Packages]# ls *.rpm
.....
zziplib-devel-0.13.62-5.el7.i686.rpm
zziplib-devel-0.13.62-5.el7.x86_64.rpm
zziplib-utils-0.13.62-5.el7.x86_64.rpm
[root@localhost Packages]# ls *.rpm |sed -nr 's/(.*)\.(.*)\.rpm$/\2/p'|sort -n|uniq -c
   2223 i686
   3117 noarch
   4571 x86_64
[root@localhost Packages]# ls *.rpm|sed -r 's/(.*)\.rpm$/\1/'|sed -r 's/(.*)\.([^.]+)/\2/'|sort -n |uniq -c
   2223 i686
   3117 noarch
   4571 x86_64
[root@localhost Packages]# ls *.rpm|sed -r 's/.*\.([^.]+)\.rpm$/\1/'|sort -n |uniq -c
   2223 i686
   3117 noarch
   4571 x86_64

[root@localhost Packages]# ls *.rpm|rev|cut -d"." -f 2|rev |sort -n|uniq -c
   2223 i686
   3117 noarch
   4571 x86_64
   
'11.使用sed将/etc/fstab文件中不以#开头的行添加#'
[root@localhost ex]# sed -nr '/^#/!p' /etc/fstab |sed -r 's/(.*)/#\1/'
#UUID=ac88f5f1-a119-4eeb-a044-61bd116c5444 /                       xfs     defaults        0 0
#UUID=04be53f7-9583-497d-8668-dc0e694be23b /boot                   xfs     defaults        0 0
#UUID=6bcd3943-2a8d-4117-83e6-4b11afc2640a /data                   xfs     defaults        0 0
#UUID=70c96873-6113-4b55-91d0-9d70a890d1d5 swap                    swap    defaults        0 0
#/dev/sr0				/mnt/cdrom		iso9660   defaults        0 0
[root@localhost ex]# sed -nr '/^[^#]/s/(.*)/#\1/p' /etc/fstab 
#UUID=ac88f5f1-a119-4eeb-a044-61bd116c5444 /                       xfs     defaults        0 0
#UUID=04be53f7-9583-497d-8668-dc0e694be23b /boot                   xfs     defaults        0 0
#UUID=6bcd3943-2a8d-4117-83e6-4b11afc2640a /data                   xfs     defaults        0 0
#UUID=70c96873-6113-4b55-91d0-9d70a890d1d5 swap                    swap    defaults        0 0
#/dev/sr0				/mnt/cdrom		iso9660   defaults        0 0


awk命令:文本分析工具(文本内容-只能匹配不能修改)

简介
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。

awk工作原理
#awk  [options]  'BEGIN{action;...}  pattern{action;..} END{action;...}'  file..
第一步:
执行BEGIN{action;..}语句块中的语句。
第二步:
从文件或标准输入(stdin)读取一行,然后执行pattern{action;..}语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,知道文件全部被读取完毕。
第三步:
当读至输入流末尾时,执行END{action;..}语句块。

BEGIN、END、pattern语句块:
BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
END语句块在awk从输入流中读取完所有的行之后再被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
pattern语句块中的通用命令时最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{print},即打印每一个读取到的行,awk读取的每一行都会执行该语句块。

在awk中分隔符、域和记录:

1.awk默认的分隔符为空白符,当以空白符做分隔符时,不用指定-F分隔符,且自动"去重"空白符。空白符包括空格、TAB键、回车、换行...等;
2.awk执行时,由分隔符分隔的字段(域)标记$1,$2,...$n称为域标记。$0为所有域。注意:和shell中变量$符含义不同。
3.省略action,则默认执行print $0的操作。 
4.行row:也称记录record(在awk中, 称为记录更合适些,一般来说,记录=)
5.列:也称域field,字段,属性

注意:在下列中,当以":"做分隔符时,cc和dd算是$3域的,不在一行,因此在这种情况下,记录更为合适。
在这里插入图片描述

调用awk方式:

'1.命令行方式:'
	在awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。$0则表示所有域,$1表示第一个域,$n表示第n个域。默认域分隔符是"空白键""[tab]键"#awk 'BEGIN {action}  /pattern/{action}  END {action}'   input-file
awk工作流程是这样的:先执行BEGING,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。
'2.shell脚本方式:'
将所有的awk命令插入一个文件,并使awk程序可执行,然后awk命令解释器作为脚本的首行,一遍通过键入脚本名称来调用。
	相当于shell脚本首行的:#!/bin/sh
	可以换成:#!/bin/awk
'3.将所有的awk命令插入一个单独文件,然后调用:'
#awk -f awk-program-file input-file(s)
其中,-f选项加载awk-script-file中的awk脚本'program',input-file(s)跟上面的是一样的。

awk命令行方式基本用法:

#awk [options] ‘program’ file…
#awk [options] ‘BEGIN{action;…} pattern{action;…} END{action;…}’ file…

'[options]':
-F:指明输入时用到的字段分隔符。
-v  var=value  :自定义变量。
-f programfile:加载programfile文件中的awk的program。

program:pattern{action statements;...}
	'pattern''action':
		pattern决定了动作语句何时触发及触发事件。
			BEGIN,pattern,END
		action statements是对数据进行处理,放在{}内指明。
			print,printf,运算符,条件语句等。
'pattern'
pattern:根据pattern条件,过滤匹配的行,再做处理。
(1)如果未指定:空模式,匹配每一行。
(2)/pattern/:仅处理能够模式匹配到的行,需要用/ /括起来。
	#awk '/^UUID/{print $1}'  /etc/fstab
	#awk '!/^UUID/{print $1}' /etc/fstab
(3)pattern:关系表达式,结果为“真”,才会被处理。
	真:结果为非0值,非空字符串。
	假:结果为0值或为空字符串。
(4)line ranges:行范围
	/part1/,/part2/:不支持直接给出数字格式。
(5)BEGIN/END模式
	BEGIN{}:仅在开始处理文件中的文本之前执行一次。
	END{}:仅在文本处理完成之后执行一次。
#awk -F: '/^root\>/,/^nobody\>/{print $1}'  /etc/passwd
#awk -F: '(NR>=10&&NR<=20){print NR,$1}' /etc/passwd
#awk -F: '/^root\>/{print $1,$5}'  /etc/passwd
#awk -F: '$3==0{print $1}'  /etc/passwd
#awk -F: '($3==0){print $1}'  /etc/passwd
#awk -F: '!($3<=500){print $1}'  /etc/passwd
#awk -F: 'BEGIN{print "USER USERID"} {print $1":"$3} END{print "end file"}'  /etc/passwd
#awk -F: '{print "USER USERID";print $1":"$3;print "end file"}'  /etc/passwd

'action'
常用的action分类:
(1)算术,比较表达式等;
(2)if,while等语句;
(3)组合语句;
(4)print,printf,system命令;
print命令
print格式:{print item1,item2...}
要点:
(1):逗号做分隔符(显示的输出分隔符为空白符)。
(2):指定输出分隔符{item1"输出分隔符"item2}
(3):输出的各item可以是字符串、数值、当前记录的字段、变量或awk表达式。
(4):若省略item,相当于{print $0}
	#awk  -F: '{print}'  /etc/passwd
	#awk  -F: '{print $1"--"$3}'  /etc/passwd
	#awk  '{print "hello,awk"}'
printf命令:
格式化输出:{printf "format",item1,item2,...}
	(1):必须制定format格式符;
	(2):不会自动换行,需要显示给出换行控制符,\n。
	(3):format中需要分别为后面每个item指定格式符。
格式符:format与item一一对应。(只有一对一)
	%c:显示为字符的ASCII码。
	%d,%i:显示为十进制整数。
	%e,%E:显示为科学计数法数值。
	%f:显示为浮点数。
	%g,%G:以科学计数法或浮点形式显示数值。
	%s:显示字符串。
	%u:无符号整数。
	%%:显示%自身
修饰符:
	'±#.#:第一个#(数字)控制显示的宽度;第二个#(数字)表示小数点后精度,如"3.1",即%3.1f。'
	-:左对齐(默认右对齐),%-15.2s。
	+:显示数值的正负符号,%+d。
#awk -F: '{printf "%s\n",$1}'  /etc/passwd
#awk -F: '{printf "%s %s\n",$1,$3}' /etc/passwd
#awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
#awk -F:  '{printf "Username:%s,UID:%d\n",$1,$3}'  /etc/passwd

system命令:awk中调用shell命令
注意:空格是awk中的字符串连接符。如果system中需要使用awk中的变量,可以使用空格分隔;或者说除了awk的变量外,其他一律用""引用起来。
	#awk BEGIN'{system("homtname")}'
	#awk BEGIN'{score=100;system("echo your socre is " score)}'
	#df -h|awk -F% '/^\/dev\/sd/{print $1}'|awk '{if($NF>20)system("date")}'

awk 程序的(‘program’)通常由:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成。其中,program通常是被单引号或双引号中,使用awk语言编辑。

awk变量:

变量:内置变量和自定义变量
内置变量:
FS:输入字段分隔符,默认为空白字符。
	#awk -v FS=':' '{print $1,FS,$3}'  /etc/passwd   //该例中':'即做输入字段分隔符又做输出字段分隔符。
	#awk  -F: '{print $1,$3,$5}'  /etc/passwd  //-F指定输入分隔符,或-v FS=''通过变量指定输入分隔符。
OFS:输出字段分隔符,默认为空白字符。
	#awk -v FS=':'  -v OFS="$" '{print $1,$3,$5}'  /etc/passwd
RS:输入记录分隔符,指定输入时的换行符。
	#awk  -v RS=':' '{print}'  /etc/passwd
ORS:指定输出记录分隔符,输出时用指定符号代替换行符。
	#awk -v RS='\n' -v ORS='###' '{print}'  /etc/passwd
NF:字段数量,列数。
	#awk -F:  '{print NF}'  /etc/passwd
	#awk -F:   '{print$(NF-1)}'  /etc/passwd
NR:记录号,可以理解为行号。
	#awk '{print NR}' /etc/fstab
	#awk 'END{print NR}' /etc/passwd
FNR:各文件分别计数,记录号。
	#awk '{print FNR}' /etc/fstab /etc/passwd
FILENAME:当前文件名
	#awk '{print FILENAME}'  /etc/passwd
ARGC:命令行参数的个数
	#awk '{print ARGC}'  /etc/fstab /etc/inittab
ARGC:数组,保存的是命令行所给定的各参数
	#awk 'BEGIN{print ARGV[0]}'  /etc/fstab /etc/passwd
	#awk 'BEGIN{print ARGV[1]}'  /etc/fstab /etc/passwd
自定义变量(区分字符大小写)
(1)-v var=value
(2)在program中直接定义
	#awk  -v test="hello word" '{print test}'  /etc/fstab
	#awk  -v test="hello word" 'BEGIN{print test}'  /etc/fstab
	#awk  'BEGIN{test="hello,word";print test}'
	#awk -F: '{test="username";print test":"$1}'  /etc/passwd
	

操作符

算术操作符:x+y,x-y,x*y,x/y,x^y,x%y。
	-x:转换为负数
	+x:转换为数值
字符串操作符:没有符号的操作符,字符串连接。
赋值操作符:=,+=,-=,*=,/=,%=,^=,++,--。
	#awk 'BEGIN{i=0;print ++i,i}'
	#awk 'BEGIN{i=0;print i++,i}'
比较操作符:==!=>>=<<=#awk -F: '($3>1000){print $1,$3}'  /etc/passwd
模式匹配符:
	~:左边是否和右边匹配包含。包含
	!~:是否不匹配。不包含
	#awk  -F: '$0 ~ /root/{print $1}'  /etc/passwd
	#awk -F: '$0 ~ "^root"{print $1}' /etc/passwd
	#awk  '$0 !~ /root/'  /etc/passwd
	#awk -F: '$3==0' /etc/passwd
逻辑操作符:与&&,或||,非!
	#awk -F: '$3>900 && $3<1000{print $1,$3}'  /etc/passwd
	#awk -F: '$3>900 || $3<1000{print $1,$3}'  /etc/passwd
	#awk -F: '!($3==0){print $1}'  /etc/passwd
	#awk -F: '!($3<=500){print $1}'  /etc/passwd
函数调用:function_name(arg1,arg2,...)
条件表达式(三目表达式):
	selector?if-true-expression:if-false-expression
	如果selector条件判断为真,则执行if-ture--,如果条件判断为假,则执行if-false--
	#awk -F: '{$3 >=1000?usertype="Common user":usertype="Sysadmin";printf "%15s:%-s\n",$1,usertype}'  /etc/passwd

awk高级用法:即awk控制语句

语法:
{statements;...}组合语句----->'{语法}'
if(condition){statement;...}
if(condition){statement;...} else{statements;..}
while(conditon){statments;..}
do{statements;..}while(condition)
for(expr1;expr2;expr3){statements;..}
break
continue
delete array[index]
delete array
exit
'----------------------------------'
'if、if-else、if-else if-else;
使用场景:-->对awk取得的整行或某个字段做条件判断'
例:
[root@localhost ~]# awk -F: '{if($3>=1000){print $1,$3}}'  /etc/passwd
[root@localhost ~]# awk -F: '{if($NF == "/bin/bash"){print $1,$NF}}'  /etc/passwd
[root@localhost ~]# awk -F: '{if($3>=1000){printf "commond user:%s\n",$1} else{printf "root or sysuser:%s\n",$1}}' /etc/passwd
[root@localhost ~]# df -h|awk -F% '/^\/dev\/sd/{print $1}'|awk '($NF>20){print $1,$5}'
[root@localhost ~]# df -h|awk -F% '/^\/dev\/sd/{print $1}'|awk '{if($NF>20){print $1,$5}}'
[root@localhost ~]# awk 'BEGIN{test=100;if(test>90){print "very good"} else if(test>60){print "good"} else{print "no pass"}}'
'while循环
条件“真”,进入循环;条件“假”,退出循环。
使用场景:-->对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用。
语法:while(conditon){statments;..}'
例:
[root@localhost ex]# awk '/^[[:space:]]*linux16/' /etc/grub2.cfg 
	linux16 /vmlinuz-3.10.0-862.el7.x86_64 root=UUID=ac88f5f1-a119-4eeb-a044-61bd116c5444 ro crashkernel=auto rhgb quiet LANG=en_US.UTF-8
	linux16 /vmlinuz-0-rescue-e1b76d47681143e68f3a92f6c41b113e root=UUID=ac88f5f1-a119-4eeb-a044-61bd116c5444 ro crashkernel=auto rhgb quiet
# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i;i++}}' /etc/grub2.cfg 
# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg 
# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){if(length($i)<=10) {print $i,length($i)};i++}}' /etc/grub2.cfg
'do-while循环
意义:-->无论真假,至少执行一次循环体
语法:do{statements;..}while(condition)'
例:
# awk 'BEGIN{total=0;i=0;do{total+=i;i++}while(i<=100);print total}'

'for循环
语法:for(expr1;expr2;expr3){statements;..}
常见用法:for(变量赋值;变量使用场景;迭代过程){statements;..}
特殊用法:遍历数组中的元素-->for(var in aray){statements;..}'
例:
# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++){print $i}}' /etc/grub2.cfg
# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++){print $i,length($i)}}' /etc/grub2.cfg
# awk 'BEGIN{for(i=1;i<=1000;i++);sum+=i;{print sum}}'
# awk 'BEGIN{for(i=1;i<=1000;i++)sum+=i;{print sum}}'
# awk 'BEGIN{for(i=1;i<=1000;i++){sum+=i;print sum}}'
'switch语句
语法:switch(expression){case value1 or /REGEXP1/:statement1;case value2 or /REGEXP2/:statement2;..;default:statement}'

'break和continue'
例:
[root@localhost ex]# awk 'BEGIN{sum=0;for(i=1;i<=10;i++){if(i==5)break;sum+=i}print sum}'
[root@localhost ex]# awk 'BEGIN{sum=0;for(i=1;i<=10;i++){if(i==5)contiue;sum+=i}print sum}'

awk数组

关联数组:array[index-expressio]
index-pression:

  • (1)可使用任意字符串;字符串要使用双引号括起来。
  • (2)如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为"空串"。
  • (3)若要判断数组中是否存在某元素,要使用"index in array"格式进行遍历。
    若要遍历数组中的每个元素,要使用for循环。
  • for(var in array){for-body}
  • 注意:var会遍历array的每个索引。
    例:
awk数组练习:
[root@localhost ex]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays){print weekdays[i]}}'
Tuesday
Monday
[root@localhost ex]# awk '{print $1}' /var/log/httpd/access_log|uniq -c
    104 192.168.1.1
     11 10.11.2.3
      6 221.32.4.1
     15 123.4.2.6
     11 13.5.7.9
     10 125.234.5.8
[root@localhost ex]# awk '/^[0-9]/{ip[$1]++}END{for(i in ip){print ip[i],i}}' /var/log/httpd/access_log
15 123.4.2.6
11 13.5.7.9
104 192.168.1.1
6 221.32.4.1
10 125.234.5.8
11 10.11.2.3
[root@localhost ex]# netstat -tan|awk '/^tcp/{state[$NF]++}END{for(i in state){print state[i],i}}'
10 LISTEN
2 ESTABLISHED

[root@localhost ex]# awk '/^[0-9]/{ip[$1]++}END{for(i in ip){print i}}' /var/log/httpd/access_log|while read ip;do iptables -A INPUT -s $ip -j REJECT;done
[root@localhost ex]# iptables -vnL
Chain INPUT (policy ACCEPT 26 packets, 1584 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 REJECT     all  --  *      *       123.4.2.6            0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 REJECT     all  --  *      *       13.5.7.9             0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 REJECT     all  --  *      *       192.168.1.1          0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 REJECT     all  --  *      *       221.32.4.1           0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 REJECT     all  --  *      *       125.234.5.8          0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 REJECT     all  --  *      *       10.11.2.3            0.0.0.0/0            reject-with icmp-port-unreachable

'统计/etc/passwd中以:为分隔符,每个字符串出现的次数'
[root@localhost ex]# awk -F: '{for(i=1;i<=NF;i++){word[$i]++}}END{for(j in word)print j,word[j]}'  /etc/passwd

awk函数

'数值处理:'
 - rand():返回0和1之间的一个随机数。
 - 例:#awk 'BEGIN{rand();for(i=1;i<=10;i++)print int(rand()*100)}'

'字符串处理:'
length([s]):返回指定字符串"[s]"的长度。
sub(r,s,[t]):对t字符串进行搜索,r表示为模式匹配的内容,并将第一个匹配的内容替换为s。
 - #echo "2008:08:08: 08:08:08"|awk 'sub(/:/,"-",$0)'
gsub(r,s,[t]):对t字符串进行搜索,r表示为模式匹配的内容,并全部替换为s所表示的内容。
- #echo "2008:08:08: 08:08:08"|awk 'sub(/:/,"-",$0)'
split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,...
- #netstat -tan |awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){printi,count[i]}}'
- #head -n1 /etc/passwd|awk '{split($0,arr,":")}END{for(i in arr){print i,arr[i]}}'

'自定义函数:'
格式:
	function name(parameter,parameter,..)
	{
			statements
			return expression
	}		
例:#cat fun1.awk
	function max(v1,v2)
	{
		v1>v2?var=v1:var=v2
		return var
	}
	BEGIN{a=3;b=2;print max(a,b)}
	#awk -f fun1.awk
	#cat fun2.awk
	function max(v1,v2)
	{
		v1>v2?var=v1:var=v2
		return var
	}
	BEGIN{print max(a,b)}
	#awk -v a=100 -v b=200 -f fun2.awk

awk脚本

将awk程序写成脚本,直接调用或执行。
例:
#ca f1.awk
	{if($3>=1000)print $1,$3}
#awk -F: -f f1.awk /etc/passwd
#cat f2.awk
	#!/bin/awk -f
	#this is a awk script
	{if($3>=1000)print $1,$3}
#chmod +x f2.awk
#f2.awk -F: /etc/passwd

向awk脚本传递参数

格式:awkfile var=value var2=value2..Inputfile
注意:在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数。
例:
#cat test.awk
	#!/bin/awk -f
	{if($3>=min && $3<=max)print $1,$3}
#chmod +x test.awk
#./test.awk -F: min=100 max=200 /etc/passwd(√)
#./test.awk -F: -v min=100 -v max=200 /etc/passwd(√)

练习:

[root@localhost ~]#  awk -F: '($3>1000){print $1,$3}'  /etc/passwd
app 1001
appq 1002
rooter 1003
bash 1004
[root@localhost ~]# awk -F: '$3>900 && $3<1000{print $1,$3}'  /etc/passwd
polkitd 999
libstoragemgmt 998
chrony 997
appd 996

你可能感兴趣的:(Linux有关文本处理的三剑客(grep、sed、awk))