Shell对输出流的处理---awk命令
在日常计算机管理中,总会有很多数据输出到屏幕或者文件,这些输出包含了标准输出、标准错误输出。默认情况下,这些信息全部输出到默认输出设备---屏幕。然而,大量的数据输出中,只有一小部分是我们需要重点关注的,把我们需要的或关注的这些信息,过滤或者提取,以备后续需要是调用。早先的学习中,我们学过使用 grep
来过滤这些数据,使用 cut
、tr
命令提取某些字段,但是它们都不具备提取并处理数据的能力,都必须先过滤,再提取转存到变量,然后再通过变量提取去处理。
比如:内存使用率的统计步骤
通过
free -m
提取出内存总量,赋值给变量 memory_totle通过
free -m
提取出内存使用量,赋值给变量 memory_use通过数学运算,计算出内存使用率
需要执行多步才能得到内存使用率,那么有没有一个能够集过滤、提取、运算为一体的命令呢?当然,就是我们即将学习的 awk
命令。
awk 介绍
awk
是一种可以处理数据、产生格式化报表的语言,功能十分强大。awk
认为文件中的每一行是一条记录,记录与记录之间的分割符为换行符;每一列是一个字段,字段与字段的分割符默认是一个或多个的空格或 tab 制表符。
awk
的工作方式是读取数据,将每一行数据视为一条记录(record),每条记录以字段分割符分割成若干个字段,然后输出各个字段的值。
awk 命令格式
[root@localhost ~]# awk [选项] [BEGIMN]{program}[END] [file]
选项:
-F :fs指定描绘一行中数据字段的分割符,默认为空格
-f :指定读取程序的文件名
-v :定义awk程序中使用的变量和默认值
程序运行优先级:
BEGIN :在开始处理数据之前执行,可选项
program :如何处理数据流,必选项
END :处理完数据流后执行,可选项
注意:
awk 程序脚本由左大括号和右大括号定义。脚本命令必须防止在两个大括号之间。由于 awk 命令行假定脚本是单文本字符串,还必须将脚本放在单引号中。
awk 的基本用法
awk 对字段的提取(列)
字段提取:提取一个文本中的一列数据并打印输出 字段相关内置变量
$0 表示正行文本
$1 表示文本行中的第一个数据字段
$2 表示文本行中的第二个数据字段
$N 表示文本行中的第N个数据字段
$NF 表示文本行中的最后一个数据字段
- 读入test文件每行数据并把每行数据打印出来
[root@linux ~ ]# awk '{print $0}' test
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
- 打印test文件的第六个字段
[root@linux ~ ]# awk '{print $6}' test
jumps
jumps
jumps
jumps
jumps
- 打印test文件的最后一个字段
[root@linux ~ ]# awk '{print $NF}' test
dog
dog
dog
dog
dog
awk 对记录的提取(行)
记录提取:提取一个文本中的一行并打印输出 记录的提取方法有两种:
- 通过行号
- 通过正则匹配
- NR:指定行号
提取 test 中的第三行(指定行号为3)
[root@localhost ~ ]# awk 'NR==3{print $0}' test
3 the quick brown fox jumps over the lazy cat . dog
[root@localhost ~ ]# awk 'NR==1{print $1,$3,$5}' /etc/passwd
root 0 root
[root@localhost ~ ]# awk 'NR==1{print $1 "-" $3 "-" $5}' /etc/passwd
root-0-root
awk 命令选项详解
- -F:指定字段与字段的分割符
当输出的数据流字段格式不是 awk
默认的字段格式时,我们可以使用 -F 命令选项来重新定义数据流字段分割符号。比如:
[root@localhost ~ ]# awk -F ':' '{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash
daemon 1 /usr/sbin/nologin
......
karin 1000 /bin/bash
sshd 122 /usr/sbin/nologin
- -f:如果
awk
命令是日常重复工作,而又没有太多变化,可以将程序写入文件,每次使用-f调用程序文件就可以了,方便且高效。
[root@localhost ~ ]# vim abc
{print $1,$1,$NF}
[root@localhost ~ ]# awk -f abc test
1 quick dog
2 quick dog
3 quick dog
4 quick dog
5 quick dog
- -v:定义变量,既然作者写awk的时候就是按着语言去写的,那么语言中最重要的要素---变量肯定不能缺席,所以可以使用 -v 命令选项定义变量
[root@localhost ~ ]# awk -v name='root' 'BEGIN{print name}'
root
awk 对字符串的提取(行列交汇)
记录和字段的汇合点就是字符串 打印 test 第三行的第六个字段
[root@localhost ~ ]# awk 'NR==3{print $6}' test
jumps
awk 程序的优先级
关于 awk
程序的优先级,BEGIN 是优先级最高的代码块,是在执行 program 之前执行的,不需要提供数据源,因为不涉及到任何数据处理,也不依赖于 program 代码块;program 代码块是对数据流干什么,是必选代码块,也是默认代码块。所以在执行时必须提供数据源;END 是处理完数据流后的操作,如果需要执行 END 代码块,就必须需要 program 的支持,单个无法执行。
优先级显示
[root@localhost ~ ]# awk 'BEGIN{print "hello world"}{print $0}END{print "bye bye"}' test
hello world
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
bye bye
- 不需要数据源,可以直接执行
[root@localhost ~ ]# awk 'BEGIN{print "hello world"}'
hello world
- 没有提供数据源,所以无法执行成功
[root@localhost ~ ]# awk '{print "hello world"}'
[root@localhost ~ ]# awk 'END{print "hello world"}'
awk 高级用法
awk
是一门语言,那么就会符合语言的特性,除了可以定义变量外,还可以定义数组,还可以进行运算,流程控制。
awk 定义数组
数组定义方式:
数组名[索引]=值
定义数组 array,有两个元素,分别是 100,200,打印数组元素。
[root@localhost ~ ]# awk 'BEIGN{array[0]=100;array[1]=200;print array[0],array[1]}'
100 200
awk 运算
赋值运算 =
比较运算 >、>=、==、<=、<、!=
数学运算 +、-、*、/、%、**、++、--
逻辑运算 &&、||
匹配运算 、!
- 赋值运算:主要是对变量或者数组赋值
[root@localhost ~ ]# read -2 /proc/meminfo | awk 'NR==1{t=$2}NR==2{f=$2;print (t-f)*100/t "%"}'
8.7528%
- 比较运算:如果比较字符串则按 ascii 编码顺序表比较,比较结果 1 则为真,0 则为假
[root@localhost ~ ]# awk 'BEGIN{print "a" >= "b" }'
0
[root@localhost ~ ]# awk 'BEGIN{print 99 >= 1 }'
1
- 数学运算:支持幂运算,支持小数点
[root@localhost ~ ]# awk 'BEGIN{print 9-1 }'
8
[root@localhost ~ ]# awk 'BEGIN{print 100%3 }'
1
- 逻辑运算:与、或运算
[root@localhost ~ ]# awk 'BEGIN{print 100>3 && 28<=9 }'
0
- 匹配运算:精确匹配 ,精确不匹配 !;模糊匹配 ~,模糊不匹配 !~
[root@localhost ~ ]# awk -F: '$1=="root"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
awk 环境变量
变量 | 描述 |
---|---|
FIELDWIDTHS | 以空格分隔的数字列表,用空格定义每个数据字段的精确宽度 |
FS | 输入字段分隔符号 |
OFS | 输出字段分隔符号 |
RS | 输入记录分隔符号 |
ORS | 输出记录分隔符号 |
FIELDWIDTHS:重新定义列宽并打印,注意不可以使用 $0 打印所有,因为 $0 是打印本行全内容,不会打印你定义的字段
[root@localhost ~ ]# awk 'BEGIN{FIELDWIDTHS="5 2 8"}NR==1{print $1,$2,$3}' /etc/passwd
root: x: 0:0:root
FS:指定数据源中字段分隔符,类似命令选项 -F
[root@localhost ~ ]# awk 'BEGIN{FS=":"}NR==1{print $1,$3,$NF}‘ /etc/passwd
root 0 /bin/bash
OFS:指定输出到屏幕后字段的分隔符
[root@localhost ~ ]# awk 'BEGIN{FS=":";OFS="-"}NR==1{print $1,$3,$NF}' /etc/passwd
root-0-/bin/bash
RS:指定记录的分隔符,将记录的分隔符修改为空后,所有的行会变成一行
[root@localhost ~ ]# awk 'BEGIN{RS=""}{print $1,$2,$3}' num
1 2 3
ORS:输出到屏幕后记录的分隔符,默认为回车
[root@localhost ~ ]# awk 'BEGIN{RS="";ORS="*"}{print $1,$2,$3,$4,$5}' num
1 2 3 4 5*[root@localhost ~ ]#
#可以看出,提示符和输出在一行了,因为默认回车换成了*(ORS="*")
流程控制
- if 判断语句
- for 循环语句
- while 循环语句
- do...while 语句
- 循环控制
- if 判断语句
[root@localhost ~ ]# awk '{if ($1<5)print $1*2;else print $1/2}' num
2
4
6
8
2.5
3
3.5
4
4.5
5
- for 循环语句
[root@localhost ~ ]# vim num2
60 50 100
150 30 10
70 100 40
[root@localhost ~ ]# awk '{sum=0;for (i=1;i<4;i++){sum+=$i}print sum}' sum2
210
190
210
- while 循环语句
[root@localhost ~ ]# awk '{sum=0;i=1;while(i<4){sum+=$i;i++}print sum}' num2
210
190
210
- do...while 语句
[root@localhost ~ ]# awk '{sum=0;i=1;do{sum+=$i;i++}while(i<4);print sum}' num2
210
190
210
- 循环控制语句
break:跳出循环,继续执行后续语句。
continue:停止本次循环,继续下一次循环。
累加每行数值,和大于 150 停止累加
[root@localhost ~ ]# awk '{
> sum=0
> i=1
> while (i<4){
> sum+=$i
> if (sum>150){
> break
> }
> i++
> }
> print sum
> }' num2
210
180
170
awk小技巧
- 打印 test 文本的行数
[root@localhost ~ ]# awk 'END{print NR}' test
5
- 打印 test 文本最后一行内容
[root@localhost ~ ]# awk 'END{print $0}' test
5 the quick brown fox jumps over the lazy cat . dog
- 打印 test 文本列数
[root@localhost ~ ]# awk 'END{print NF}' test
12