shell脚本 之 awk详解(数据筛选)

1. awk命令简介

awk是专门为文本处理设计的编.程语言,也是一个应用程序,几乎所有Linux发行版本都自带这个程序。我们通常用它进行数据扫描、过滤、统计汇总工作。

  • awk是一种强大的编辑工具,比较倾向于一行当中分成数个字段来处理,因为awk相当适合小型的文本数据。
  • awk 比较倾向于将一行分成多个字段然后再进行处理,且默认情况下字段的分隔符为空格或 tab 键。awk 执行结果可以通过 print 的功能将字段数据打印显示。

awk工作原理

  • 逐行读取文本,默认以空格或tab键为分隔符进行分隔,将分隔所得的各个字段保存到内建变量中,并按模式或者条件执行编辑命令。
  • 在使用awk命令的过程中,可以使用逻辑操作符“&&”表示“”、“||”表示“”、“!”表示“”;还可以进行简单的数学运算,如+、-、*、/、%^分别表示加、减、乘、除、取余乘方

2. awk命令格式

它与grepsed命令一样都是以行为单位读取文本的,默认以空格或Tab键为分隔符,将分割所得的各个字段保存到内建变量中供后续使用

命令格式如下:

awk "[选项1] {操作1;操作2} [选项2] {操作3;操作4} ..." 文件名
awk -f 脚本文件 文件名

awk语法由一系列选项和操作组成,在花括号内可以有多个操作,在多个操作之间是有分号分隔,在多个选项和操作之间可以有若干空格,也可以没有

选项说明:
-F fs or --field-separator fs:相当于内建变量“FS”,指定分割符为fs,默认为空格或Tab制表符;
-f scripfile or --file scriptfile:从文件中读取awk指令,用来代替命令行中输入的命令;
-v var=value or --asign var=value:设置一个变量并且附上初值。

3. awk常见的内置变量和运算符

常见的内置变量如下

内置变量 说明
$0 当前记录(作为单个变量),即当前处理的行的字段个数
FS 列分割符指定每行文本的字段分隔符,默认为空格或制表位。也可用选项"-F"表示
NF 当前处理行的字段个数,即列数,从1开始
NR 当前记录中的行数,从1开始
$n 当前处理行的第n个字段(第n列)
RS 行分隔符。awk从文件上读取资料时,将根据RS的定义把资料切割成许多条记录,而awk一次仅读入一条记录,以进行处理。预设值是’\n’
FILENAME 被处理的文件名
FNR 当awk处理多个文件时,分别对每个文件的行数进行计数
ORS 更改行换行符
OFS 更改列分割符
ARGV 表示是一个数组,这个数组中保存的是命令行所给定的参数
ARGC 表示参数的数量,也可以理解为ARGV数组的长度

运算符

运算符 描述
= += -= *= /= %= ^= **= 赋值
?: C语言中的三目运算符
< <= > >= != == 关系运算
+ - * / % 算术运算
^ ** 求幂
++ -- 自增和自减
||和&& 逻辑或/与
$ 字段引用
in 数组成员

4. awk常见用法汇总

4.1 基础用法

示例,编写一个test.txt文本

root@ubuntu:/home/zgx/Desktop# cat test.txt    # 查看文本内容
this is a test file
hello,world!
bye byeha
year, month, day, hour, minute, second
root@ubuntu:/home/zgx/Desktop# awk "{print NF}" test.txt     # 使用NF显示每行的列数
5
1
2
6
root@ubuntu:/home/zgx/Desktop# awk "{print $NR}" test.txt     # 打印显示当前每行记录
this is a test file
hello,world!
bye byeha
year, month, day, hour, minute, second

4.1.1 打印指定列的内容

使用$1,$4打印第1列、第4列的内容

root@ubuntu:/home/zgx/Desktop# awk '{print $1,$4}' test.txt   # 普通输出
this test
hello,world!
bye 
year, hour,
root@ubuntu:/home/zgx/Desktop# awk '{printf "%-10s %-10s\n",$1,$4}' test.txt   # 格式化输出
this       test      
           !
bye                  
year,      hour,  

注意:print与printf的区别:
1)printf用于格式化输出
2)printf默认是不带换行符号的,所以要换行需要带上\n

4.1.2 打印指定分隔符的内容

指定英文逗号“,”为分隔符

root@ubuntu:/home/zgx/Desktop# awk -F',' '{print $1,$4}' test.txt
this is a test file
hello 
bye byeha
year  hour

4.1.3 打印多个分隔符的内容

指定多个分割符输出(首先使用空格,然后在使用其他分割符)

root@ubuntu:/home/zgx/Desktop# awk -F '[, ]' '{print NF}' test.txt   # 查看分隔后的每行列数
5
2
2
11
root@ubuntu:/home/zgx/Desktop# awk -F '[, ]' '{print $1,$2,$3}' test.txt
this is a
hello world!
bye byeha
year [空格] month

4.2 进阶用法

4.2.1 打印指定行的内容

打印第i到第j行的内容(两种方法)

root@ubuntu:/home/zgx/Desktop# awk 'NR==2,NR==4{print}' test.txt   # 打印第2到第4的内容
root@ubuntu:/home/zgx/Desktop# awk 'NR==2,NR==4' test.txt     # {print}可要可不要
hello,world!
bye byeha
year, month, day, hour, minute, second

root@ubuntu:/home/zgx/Desktop# awk '(NR>=2)&&(NR<=4){print}' test.txt   # 打印第2到第4的内容
root@ubuntu:/home/zgx/Desktop# awk '(NR>=2)&&(NR<=4)' test.txt   # {print}可要可不要
hello,world!
bye byeha
year, month, day, hour, minute, second

打印第i、第j行的内容

root@ubuntu:/home/zgx/Desktop# awk 'NR==2 || NR==4' test.txt    # 打印第2、第4行的内容
hello,world!
year, month, day, hour, minute, second

4.2.2 支持正则表达式

显示text2.txt文本内容如下

root@ubuntu:/home/zgx/Desktop# cat test2.txt
this is a test file
hello,world!
bye byeha
year, month, day, hour, minute, second
hahaha
hello everybody
aaaaaaaaa

输出以h、he开头的行

root@ubuntu:/home/zgx/Desktop# awk '/^h/{print}' test2.txt   # 打印以h开头的行
hello,world!
hahaha
hello everybody
root@ubuntu:/home/zgx/Desktop# awk '/^he/' test2.txt   # 打印以he开头的行
hello,world!
hello everybody

输出以d!为结尾的行

root@ubuntu:/home/zgx/Desktop# awk '/d!$/' test2.txt
hello,world!

输出第2列包含字符b的行

root@ubuntu:/home/zgx/Desktop# awk '$2 ~ /b/' test2.txt
bye byeha
hello everybody
root@ubuntu:/home/zgx/Desktop# awk '$2 !~ /b/' test2.txt    # 输出第2列不包含字符b的行
this is a test file
hello,world!
year, month, day, hour, minute, second
hahaha
aaaaaaaaa

4.2.3 根据运算符过滤指定的行

显示test3.txt文本的内容如下:

root@ubuntu:/home/zgx/Desktop# cat test3.txt
101 this is a test file
102 hello world!
203 bye byeha
204 year, month, day, hour, minute, second
205 fawff
no number
206,aaaaaaaa

输出第i列数值在指定范围的行

root@ubuntu:/home/zgx/Desktop# awk '$1>200' test3.txt    # 输出第1列数值大于200的行
203 bye byeha
204 year, month, day, hour, minute, second
205 fawff
no number
206,aaaaaaaa
root@ubuntu:/home/zgx/Desktop# awk '$1>300' test3.txt     # 输出第1列数值大于300的行
no number

输出第1列小于100且第2列字符串为“hello”的行

root@ubuntu:/home/zgx/Desktop# awk '$1<200 && $2=="hello"' test3.txt
102 hello world!

4.2.4 输出与指定内容相反的行

输出以非h开头的行

root@ubuntu:/home/zgx/Desktop# awk '!/^h/' test2.txt   # 打印以非h开头的行
this is a test file
bye byeha
year, month, day, hour, minute, second
aaaaaaaaa

输出第1列数值非大于200的行

root@ubuntu:/home/zgx/Desktop# awk '!($1>200)' test3.txt
101 this is a test file
102 hello world!

4.2.5 设置变量

root@ubuntu:/home/zgx/Desktop# awk -F'[, ]' -va=1 -vb=sss '{print $1,$1+a,$1a,$1b}' test3.txt
101 102 1011 101sss
102 103 1021 102sss
203 204 2031 203sss
204 205 2041 204sss
205 206 2051 205sss
no 1 no1 nosss
206 207 2061 206sss

4.3 字符串函数

函数 说明
length() 返回字符串的长度
index() 返回下标
tolower() 转换成小写并返回字符串
toupper() 转换成大写并返回字符串
substr() 返回字符串中的子串
match() 返回下标,但它不搜索子串
sub() 替换匹配的第一个字符串序列,并返回整个字符串
gsub() 替换匹配的所有字符串序列,并返回整个字符串
split() 分割字符串并将各部分放到使用整数下标的数组中

打印各行字符串的长度

root@ubuntu:/home/zgx/Desktop# cat test2.txt
this is a test file
hello,world!
bye byeha
year, month, day, hour, minute, second
hahaha
hello everybody
aaaaaaaaa
root@ubuntu:/home/zgx/Desktop# awk '{print length()}' test2.txt
19
12
9
38
6
15
9

打印字符串长度超过15的行

root@ubuntu:/home/zgx/Desktop# awk 'length>15' test2.txt
this is a test file
year, month, day, hour, minute, second

字符串大小写转换

root@ubuntu:/home/zgx/Desktop# awk '{print $1, tolower($2), toupper($3)}' test2.txt
this is A
hello,world!  
bye byeha 
year, month, DAY,
hahaha  
hello everybody 
aaaaaaaaa 

打印每行的最后两个字符

root@ubuntu:/home/zgx/Desktop# awk '{print substr($0,length($0)-1)}' test2.txt
le
d!
ha
nd
ha
dy
aa

4.4 BEGIN/END关键字

默认情况下awk是从输入中读取一行字符串,然后对该行执行相应的命令,而有时候想在执行这些命令之前先进行一些其他的脚本命令,则可以使用BEGIN关键字,而END关键字的处理时机则刚好相反。
代码示例:

root@ubuntu:/home/zgx/Desktop# awk 'BEGIN{print "---file start---"} {print $1,$4} END{print "---file end---"}' test2.txt
---file start---
this test
hello,world! 
bye 
year, hour,
hahaha 
hello 
aaaaaaaaa 
---file end---

你可能感兴趣的:(shell,linux,运维,服务器)