awk 命令
Shell 处理文本的三剑客 —— grep、awk、sed
awk 说明
- awk 是专门对字符串进行处理的编程语言 —— 具有编程语言的特点
注:文本编辑器很多功能的底层逻辑都是基于 awk
awk —— 字符提取命令
-
命令格式:
- awk ‘条件{动作}’ filename —— 对文本文件内的字符串进行操作
- 命令 | awk ‘条件{动作}’ —— 对命令产生结果的字符串进行操作
-
功能定位:行提取、列提取都能做 —— grep 用于行过滤
-
数据处理过程:逐行处理 —— 每读取一行,执行一次所有动作
-
条件:通过指定条件过滤出符合条件的行
- 变量>10 —— 判断变量的值是否大于 10,可使用 >、<、>=、<=、、、
- 变量==变量 —— 判断两个变量的值是否相等,可使用 ==、!=、、、
- 变量~/字符串/ —— 判断变量中是否包含指定字符串,//、!//
-
动作:通过动作将符合条件的行打印出来,在打印的时候可以选择打印该行的哪一列
说明:awk 通过设置的具体条件过滤关键词,然后根据规定的格式输出指定的内容
例1:行截取
[root@localhost~]# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root 18G 1.3G 17G 8% /
devtmpfs 475M 0 475M 0% /dev
#--------------------------------------------------------------------
[root@localhost~]# df -h | awk '/\/$/{print $0}'
/dev/mapper/centos-root 18G 1.3G 17G 8% /
[root@localhost~]# df -h | grep "\/$"
/dev/mapper/centos-root 18G 1.3G 17G 8% /
注:‘’ 中的正则表达式两侧必须加 //,/$ 表示提取以 / 结尾的行,print 是输出,$0 表示整行(所有列)
例2:列截取
[root@localhost~]# df -h | cut -c 44-48
8%
100%
25%
0%
[root@localhost~]# df -h | awk '{print $5}'
已用%
8%
100%
25%
0%
注:$0 代表整行, 1 、、、 1、、、 1、、、n 代表某一列 —— awk 内置变量
说明:cut 是靠字符数截取,不能识别不规律的字符作为的列间隔,规则不同所以对齐不一致
例3:根据条件定位到行,根据动作定位到列 —— 精确定位
[root@localhost~]# cat user.list
ID NAME AGE LINUX NETWORK
1 laowang 18 59 89
2 laosong 19 99 100
[root@localhost~]# awk '$2=="laowang"{print $3}' user.list
18
[root@localhost~]# awk '$2~/laowang/{print $3}' user.list
18
说明:“$2~/laowang/” 表示第二列中,包含 “laowang” 的行
awk 的动作
awk 中的动作:{printf}
printf 说明
- printf 是标准的格式化输出,需要手动指定输出内容的类型和输出时的格式
输出格式
- printf ‘类型/格式’ 字符串
注:可以使用 cat、head、tail 等命令将文本内的字符串取出,配合管道符交由 printf 处理
补充:“| xargs” —— 传输字符串
输出类型
- %s —— 将内容按照字符串类型输出
- %i —— 将内容按照整数类型输出
- %f —— 将内容按照浮点数类型输出(%.2f:代表输出小数点数值时保留两位小数点)
注:“%” 后的整数数字表示输出字符前保留几位空格,小数用于指定浮点数保留小数位数
例:print ‘%10s’ 中的 10 表示增加的列宽,%.2f 是浮点数取小数点位数
输出格式
- \t —— 字符之间用制表符分割,即 tab 键
- \n —— 字符之间用换行符分割,即 enter 键
注:“\t” 和 “\n” 是最常用的输出格式,制表符和换行符
案例演示
-
使用 printf 输出表格文件内容
ID NAME LINUX MYSQL DOCKER 0 xcang 95 59 78 1 xbo 83 75 93 2 xlong 74 96 63 [root@localhost~]# printf '%s' `cat cut.txt` IDNAMELINUXMYSQLDOCKER0xcang9559781xbo8375932xlong749663[root@localhost~]#
说明:在使用 printf 输出时,如果仅指定输出类型而不指定输出格式,则会将所有字符输出成一整行
-
使用 printf 输出表格文件内容 —— 自定义输出格式
[root@localhost~]# printf '%s\t%s\t%s\t%s\t%s\n' `cat cut.txt` ID NAME LINUX MYSQL DOCKER 0 xcang 95 59 78 1 xbo 83 75 93 2 xlong 74 96 63
-
第 1 列使用整数类型输出,3、4、5 列使用浮点类型输出
[root@localhost~]# printf '%i\t%s\t%.2f\t%.2f\t%.2f\n' `cat cut.txt` -bash: printf: ID: 无效数字 -bash: printf: LINUX: 无效数字 -bash: printf: MYSQL: 无效数字 -bash: printf: DOCKER: 无效数字 0 NAME 0.00 0.00 0.00 0 xcang 95.00 59.00 78.00 1 xbo 83.00 75.00 93.00 2 xlong 74.00 96.00 63.00 [root@localhost~]# printf '%i\t%s\t%.2f\t%.2f\t%.2f\n' `cat cut.txt|grep -v ID` 0 xcang 95.00 59.00 78.00 1 xbo 83.00 75.00 93.00 2 xlong 74.00 96.00 63.00
说明:错误信息是因为表格第一行是字符串组成的标题无法以整数类型输出,配合 grep 命令进一步过滤
awk 的条件
预定义条件、关系运算条件、包含匹配条件、、、
awk 预定义条件
- BEGIN
- END
注:这两个条件为系统预设好的特殊条件,必须用大写
BEGIN
-
在 awk 未读取数据前声明的动作,该条件后的动作仅在程序开始时执行一次,不会重复执行
[root@localhost~]# awk 'BEGIN{printf "MYSQL成绩单:\n"}{printf $2"\t"$4"\n"}' cut.txt MYSQL成绩单: NAME MYSQL xcang 59 xbo 75 xlong 96
BEGIN 作为条件,在 awk 读取数据前输出字符串并换行
-
注意 awk 工作流程是根据每次读取到有行,就执行一次所有动作 —— 类似循环,不加 BEGIN 则会
[root@localhost~]# awk '{print "Linux 成绩单"}' cut.txt Linux 成绩单 Linux 成绩单 Linux 成绩单 Linux 成绩单 [root@localhost~]# awk '{print "Linux 成绩单"}{printf $2"\t"$3"\n"}' cut.txt Linux 成绩单 NAME LINUX Linux 成绩单 xcang 95 Linux 成绩单 xbo 83 Linux 成绩单 xlong 74
awk 根据有多少行,则执行多少次 —— 按行读取,过滤处理
-
使用 BEGIN 提前声明分隔符,FS —— 分隔符
[root@localhost~]# awk 'BEGIN{FS=":"}{printf $1"\t"$7"\n"}' /etc/passwd root /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin lp /sbin/nologin
-
若不适用 BEGIN 提前声明分隔符,那么声明分隔符只能在读取文件之后生效
[root@localhost~]# head -5 /etc/passwd | awk '{FS=":"}{printf $1"\t"$7"\n"}' root:x:0:0:root:/root:/bin/bash # 第一行并未生效 bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin lp /sbin/nologin
总结:BEGIN两种用法:声明分隔符、输出标题
END
-
类似于 BEGIN,在 awk 处理完所有数据后声明的条件,在该条件后的程序仅在程序结束前执行一次
[root@localhost~]# awk '{printf $2"\t"$5"\n"}END{printf "以上是所有人DECKER的成绩\n"}' cut.txt NAME DOCKER xcang 78 xbo 93 xlong 63 以上是所有人DECKER的成绩
说明:BEGIN 和 END 分别是在对文本进程处理之前和结束之后进行的操作,一般用做 awk 操作的说明
awk 关系运算条件
- >、<、>=、<=、==、!=
- 用来判断左右两侧的关系,一般左侧为变量,右侧为参考值
例1:列出 Linux 成绩大于 80 分的成绩单
[root@localhost~]# awk 'BEGIN{printf "列出Linux成绩大于80的成绩单\n"}$3>=80{printf $2"\t"$3"\n"}' cut.txt
列出Linux成绩大于80的成绩单
NAME LINUX
xcang 95
xbo 83
例2:列出学号为 2 号的各科成绩单
[root@localhost~]# awk '$1==2{printf $0"\n"}' cut.txt
2 xlong 74 96 63
awk 包含匹配条件 —— 包含关系
-
、!、//、!// —— 用来进行匹配包含关系,判断左侧变量中是否包含右侧的字符串
-
当右侧字符串中包含一些特殊符号时
-
需要使用 “//” 然后在里面使用 “\” 转义符将符号转义为普通字符
-
-
使用表格进行演示
[root@localhost~]# cat cut.txt ID NAME LINUX MYSQL DOCKER MAIL 0 xcang 95 59 78 xcang@163.com 1 xbo 83 75 93 boduo@126.com 2 xlong 74 96 63 zeze@gmail.com
-
以表格指定列作为变量,输出表格中指定列包含某字符的整行
[root@localhost~]# awk '$6~/x/{printf $0"\n"}' cut.txt 0 xcang 95 59 78 xcang@163.com # 第六列包含 x 的行
-
输出所有包含,字符 “x” 的行
[root@localhost~]# awk '/x/{printf $0"\n"}' cut.txt 0 xcang 95 59 78 xcang@163.com # 包含 x 的行 1 xbo 83 75 93 boduo@126.com 2 xlong 74 96 63 zeze@gmail.com
-
输出以 “.com” 作为结尾的行 —— 配合正则
[root@localhost~]# awk '$0~/\.com$/{printf $0"\n"}' cut.txt 0 xcang 95 59 78 xcang@163.com # 以 .com 结尾的行 1 xbo 83 75 93 boduo@126.com 2 xlong 74 96 63 zeze@gmail.com
-
过滤 df -h 命令,指定行过滤配合列截取,输出 “sr” 以及 “sd” 光盘和硬盘的使用占比
[root@localhost~]# df -h | awk '/(sd|sr)[a-z]?[0-9]/{printf $1"\t"$5"\n"}' /dev/sr0 100% # 匹配sd或sr... /dev/sda1 25%
说明:以行 + 列,以此确定指定内容在表中的位置,配合正则表达式,进行在文本内的精准查询
awk 的格式
awk 标准格式
- awk ‘条件{动作}’ filename —— 对文本文件内的字符串进行操作
- 命令 | awk ‘条件{动作}’ —— 对命令产生结果的字符串进行操作
多条件多动作
- awk ‘条件1{动作1}条件2{动作2}’ filename
单条件多动作
- awk ‘条件{动作1;动作2;…}’ filename
例1:多条件多动作
[root@localhost~]# awk '$4>=70{print $0}' cut.txt
ID NAME LINUX MYSQL DOCKER
1 xbo 83 75 93
2 xlong 74 96 63
[root@localhost~]# awk 'NR>1&&$4>=70{print $0}' cut.txt
1 xbo 83 75 93
2 xlong 74 96 63
注:NR>1 表示从第二行开始读取,&& 表示逻辑与运算
注:第一行也输出了,说明使用 “>” 等符号可以对字符进行对比 —— ASCII
例2:单条件多动作
[root@localhost~]# awk '{print "hello world";print $2}' cut.txt
hello world
NAME
hello world
xcang
hello world
xbo
hello world
xlong
注:多个 “条件{动作}” 可以用空格分隔,在一个动作中,如果需要执行多个命令,需要用 “;” 分隔
awk 内置变量
awk内置变量 | 作用 |
---|---|
$0 | 代表 awk 读入当前行的整行数据 |
$n | 代表 awk 读入当前行的第 n 列数据 |
NR | 代表当前 awk 正在处理的行的行号 |
NF | 代表当前 awk 读取数据总字段数(总列数) |
FS | 用来声明 awk 的分隔符,如 BEGIN{FS=“:”} |
例1:使用 NR 变量从第二行开始输出 —— “NR>1”
[root@localhost ~]# awk 'NR>1{printf $0"\n"}' cut.txt
0 xcang 95 59 78 xcang@163.com
1 xbo 83 75 93 boduo@126.com
2 xlong 74 96 63 zeze@gmail.com
例2:使用 NF 统计文件的总列数
[root@localhost ~]# awk 'END{printf "文件的总列数为:"NF"\n"}'
文件的总列数为:6 # 结尾 "\n" 换行
[root@localhost ~]# awk 'END{print NF}' cut.txt
6 # 不加 END 则会根据文件行数输出
例3:打印以 “/” 为分隔符号内容的最后一列
[root@localhost network-scripts]# echo $PWD | awk -F / '{printf $NF"\n"}'
network-scripts
补充
awk '{print NF}' cut.txt # 统计列数
awk '{print $NF}' cut.txt # $NF 表示最后一列
awk '{print $NF}' /root/anaconda-ks.cfg # 列出每一行的最后一列
awk 数值运算
awk 中默认支持数值运算,并且整数、浮点数运算都支持
[root@localhost~]# awk 'NR>1{printf $2"的平均分是\t"($3+$4+$5)/3"\n"}' cut.txt
xcang的平均分是 77.3333
xbo的平均分是 83.6667
xlong的平均分是 77.6667
[root@localhost~]# printf '%s\t%.2f\n' `awk 'NR>1{printf $2"的平均分是\t"($3+$4+$5)/3"\n"}' cut.txt`
xcang的平均分是 77.33 # 反引号取出上一条命令结果,按print格式打印
xbo的平均分是 83.67
xlong的平均分是 77.67
[root@localhost~]# c=$(awk 'BEGIN{print 7.01*5-4.01 }')
[root@localhost~]# echo $c
31.04 # 直接进行浮点数运算
注:awk 和 print 的结合,支持浮点数运算,BEGIN 表示括号里的运算只进行一次 —— 一个必要条件
awk 工作原理
awk 的工作过程
-
查看是否有 BEGIN 特殊条件
-
会在读取字符串之前先执行且仅执行一次
-
可以使用 BEGIN 声明分隔符号
-
head -5 /etc/passwd | awk 'BEGIN{FS=":"}{printf $1":"$7}'
-
-
匹配该文件的分隔符是什么 —— 空格、制表符、指定分隔符、、、
-
读取字符串的第一行(确定该行有几列)
-
将该行中每一列的字符串当作变量的值
-
$0、 1 、、、 1、、、 1、、、n —— 整行、第 1 列、、、第 n 列
-
-
查看 awk 语句中是否有条件判断,对当前行进行过滤
- 条件类型有很多,是否相等、是否包含、第几行、、、
-
若当前行匹配符合条件,则执行对应大括号内的动作
- print —— 标准输出,自动套用原有格式
- printf —— 标准格式化输出,丢失了原有的输出样式,需要自定义输出格式
-
处理完第一行后,按照 2、3、4 步骤顺序处理后续每一行
-
检查所有字符串都执行完后,是否有 END 条件
- 在所有动作之后执行,即 END 声明的为最后一个动作,仅执行一次
总结
- 先查看是否有 BEGIN 条件,有则先执行 BEGIN 后面定义动作
- 如果没有 BEGIN 条件,则先读入第一行,把第一行的数据使用分隔符分隔好之后依次赋值给变量 $0、$1、$2、$3、、、等变量
- $0 代表整行数据,$1 则为第一个字段,以此类推 —— 类似位置参数变量
- 第一行将所有内容赋值完成后,进行条件判断,按照符合条件的动作执行
- 处理完第一行之后,将第二行赋值,重复第一行的所有步骤即可,依次直到处理完整个文本
- 检查是否有 END 声明的条件,有则执行,无则结束
注意事项:在 awk 编程中,因为命令语句非常长,输入格式时需要注意一下内容
- 多个 “条件{动作}” 可以用空格分隔
- 在一个动作中,如果需要执行多个命令,需要用 “;” 分隔
- 在 awk 中,变量的赋值与调用都不需要使用 “$” 符
- 判断两个值是否相同,使用 “==” 以便和变量赋值进行区分
补充:print ‘%10s’ 中的 10 表示增加的列宽,%.2f 是浮点数取小数点位数