awk 文本处理命令

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 的工作过程

  1. 查看是否有 BEGIN 特殊条件

    • 会在读取字符串之前先执行且仅执行一次

    • 可以使用 BEGIN 声明分隔符号

    • head -5 /etc/passwd | awk 'BEGIN{FS=":"}{printf $1":"$7}'

  2. 匹配该文件的分隔符是什么 —— 空格、制表符、指定分隔符、、、

    • 读取字符串的第一行(确定该行有几列)

    • 将该行中每一列的字符串当作变量的值

    • $0、 1 、、、 1、、、 1、、、n —— 整行、第 1 列、、、第 n 列

  3. 查看 awk 语句中是否有条件判断,对当前行进行过滤

    • 条件类型有很多,是否相等、是否包含、第几行、、、
  4. 若当前行匹配符合条件,则执行对应大括号内的动作

    • print —— 标准输出,自动套用原有格式
    • printf —— 标准格式化输出,丢失了原有的输出样式,需要自定义输出格式
  5. 处理完第一行后,按照 2、3、4 步骤顺序处理后续每一行

  6. 检查所有字符串都执行完后,是否有 END 条件

    • 在所有动作之后执行,即 END 声明的为最后一个动作,仅执行一次

总结

  • 先查看是否有 BEGIN 条件,有则先执行 BEGIN 后面定义动作
  • 如果没有 BEGIN 条件,则先读入第一行,把第一行的数据使用分隔符分隔好之后依次赋值给变量 $0、$1、$2、$3、、、等变量
    • $0 代表整行数据,$1 则为第一个字段,以此类推 —— 类似位置参数变量
  • 第一行将所有内容赋值完成后,进行条件判断,按照符合条件的动作执行
  • 处理完第一行之后,将第二行赋值,重复第一行的所有步骤即可,依次直到处理完整个文本
  • 检查是否有 END 声明的条件,有则执行,无则结束

注意事项:在 awk 编程中,因为命令语句非常长,输入格式时需要注意一下内容

  • 多个 “条件{动作}” 可以用空格分隔
  • 在一个动作中,如果需要执行多个命令,需要用 “;” 分隔
  • 在 awk 中,变量的赋值与调用都不需要使用 “$” 符
  • 判断两个值是否相同,使用 “==” 以便和变量赋值进行区分

补充:print ‘%10s’ 中的 10 表示增加的列宽,%.2f 是浮点数取小数点位数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值