shell三剑客之awk-07
awk介绍
grep,sed,awk为linux的文件处理“三剑客”,各有特长。
grep:更适合单纯的==查找==(通过要查找的关键字)或匹配(通过正则)==行==
sed:更适合==编辑==文本(行删除,行打印,行增加,替换与修改等)
awk:更适合==格式化文本,对文本进行较复杂格式处理==
今天主要讨论awk,格式化文本是很专业的说法,通俗来说就是可以把文本变成你想要的样子
awk使用格式
awk -F"分隔符" "命令动作" 被处理的文件,"也可以正则匹配" 正则匹配到的内容算一个分隔符也可以通过管道传给awk处理cat 被处理的文件 | awk -F"分隔符" "命令动作"
awk内部相关变量
常用变量 | 说明 |
$0 (==常用==) | 当前处理行的所有记录(所有列数之和,包括分隔符) |
$1 到 $n (==常用==) | 文件中每行以间隔符号分割的不同字段($1代表第1列。。。。。。以此类推) |
NF (==常用==) | 当前记录的字段数(列数) |
$NF (==常用==) | 最后一列 |
NR (==常用==) | 行号 |
FS (==常用==) | 定义间隔符,等同于-F参数 |
awk的行匹配符
awk行匹配符 | 说明 |
== | 等于,完全匹配 |
~ | 匹配 |
!= | 不等于,不完全匹配 |
!~ | 不匹配 |
awk中BEGIN...END结构
在前面我们有提过awk在处理文件时是逐行处理的,那么有可能要在处理第一行前做一些事情(比如定义变量),在处理完最后一行后做一些事情(比如打印统计信息等)。这就要用到BEGIN...END结构了。
结构关键字 | |
BEGIN { } | 在awk处理文件第一行之==前==,大括号里写处理前的代码 |
{ } | 前面没有BEGIN或END的大括号,都表示逐行处理文件过程==中==要做的事,==会根据行数来循环== |
END { } | 在awk处理完了后一行之==后==,大括号里写处理前的代码 |
**问题:** 比较这两句, 结果有什么区别?(请先根据理解在脑海里想一个答案的样子,再执行命令比较一下) # head -3 /etc/passwd | awk -F: '{print "用户名\tUID"}{print $1"\t"$3}' #每行都会执行两个括号内的代码 # head -3 /etc/passwd | awk -F: 'BEGIN {print "用户名\tUID"}{print $1"\t"$3}' #以BEGIN开头的括号内代码,只在开始执行一次。 对标上述BEGIN和END理解一下,BEGIN只在开始的时候执行一次,END只在结尾的时候执行一下,大括号{}每处理一行 都是执行一次内部代码,其中$1$2 等就是当行截取的第一列及第二列的值 # cat /etc/sysconfig/network |wc -l 2 此文件有2行 # awk -F: 'BEGIN {print "aaa"} {print "bbb"} END {print "ccc"}' /etc/sysconfig/network aaa bbb bbb ccc
思路
一定要牢记awk是会逐行处理文件,所以直接看作是按行数来处理的循环
在循环前定义一个变量表示要计算的总列数,如sum=0
每行循环处理时,sum=sum+每行的列数(再次强调,awk里的运算不需要运算符)
循环处理完文件后,打印出sum的值就是想要求的总列数了
也就是说awk的处理方式就是类似下面的这个循环结构
sum=0 for i in 行数 do sum=sum+每一行的列数 done echo sum
awk的运算符
再次强调,awk是一门语言,所以有自己的运算符(注意:有些地方与shell不一样)
运算符 | 说明 |
== | 等于 和shell里不一样,shell里字符串比较是= ;数字比较是-eq |
!= | 不等于 shell里数字比较是-ne代表不等于 |
> | 大于 shell里数字比较是(-gt)代表大于 |
< | 小于 shell里数字比较是(-lt)代表小于 |
>= | 大于等于 shell里数字比较是(-ge)代表大于等于 |
<= | 小于等于 shell里数字比较是(-le)代表小于等于 |
&& | 逻辑与(和) |
|| | 逻辑或 |
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 求余数 |
打印/etc/passwd第五行(**可以把NR==5看作是一个判断的条件,满足此条件才执行print $0**) awk 'NR==5 {print $0}' /etc/passwd 打印/etc/passwd第五到十行,并在前面加上行号 awk 'NR>=5 && NR<=10 {print NR,$0}' /etc/passwd 打印第五行和第六行 awk -F: 'NR==5 || NR==6 {print $0}' /etc/passwd 打印/etc/passwd奇数行 (删除偶数行) awk 'NR%2==1 {print NR,$0}' /etc/passwd 打印/etc/passwd偶数行 (删除奇数行) awk 'NR%2==0 {print NR,$0}' /etc/passwd 对/etc/passwd里的用户做分类,分成管理员,系统用户,普通用户 # awk -F: '$3==0 {print $1}' /etc/passwd # awk -F: '$3<500 && $3>0 || $3==65534 {print $1}' /etc/passwd # awk -F: '$3>499 && $3!=65534 {print $1}' /etc/passwd
awk总体语法格式
#比如: 将/etc/passwd文件的前三行处理成下面的样子(先不要管它是怎么得到的,我们慢慢来讲解) # head -3 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin # head -3 /etc/passwd |awk -F: 'BEGIN{print"用户名\tUID"}{print $1"\t"$3}' 用户名 UID root 0 bin 1 daemon 2 # head -3 /etc/passwd |awk -F: '{print $1}' root bin daemon # head -3 /etc/passwd |awk -F: '{print $3}' 0 1 2 #awk可以将文本截取多段后自由拼接,并可以额外加字符进行粘合,而cut会比较麻烦 # head -3 /etc/passwd |awk -F":" '{print $1"用户的uid是"$3}' root用户的uid是0 bin用户的uid是1 daemon用户的uid是2 #而cut要实现同类效果会比较麻烦,cut命令无法在截取的两列中间直接加字符进行粘合,需要脚本 #!/bin/bash head -3 /etc/passwd | cut -d: -f1,3 |while read a do head=`echo $a|cut -d: -f1` tail=`echo $a|cut -d: -f2` echo "$head的uid是$tail" done 以逗号或点号为分隔符(再次回顾正则表达式里中括号里的字符任选其一),截取第2列 # echo hello,world.sed |awk -F[,.] '{print $2}' world 以逗号或点号为分隔符,将三列重新排序,表示遇到, . 都算作一个分割。 # echo hello,world.sed |awk -F[,.] '{print $2","$3"."$1}' world,sed.hello 下面这样的字符串,以点为分隔符可以做,但是点号太多,不好数.所以可以用正则表达式,以连续的多个点来为分隔符 [.]+ 表示分隔符为至少一个点或者连续多个点为分隔符 # echo "haha,hehe..............................heihei......" |awk -F"[.]+" '{print $2}' heihei 截取/etc/passwd文件的最后一列 # awk -F: '{print $NF}' /etc/passwd 截取/etc/passwd文件的倒数第二列 # awk -F: '{print $(NF-1)}' /etc/passwd #awk也可以做行匹配,$0代表每一行所有内容~代表匹配每一行是否包含root,如果包含就把这一行打印出来 awk '$0~"root" {print $0}' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin #完全匹配 查找/etc/passwd文件里用户名为root的行 awk -F: '$1=="root" {print $0}' /etc/passwd root:x:0:0:root:/root:/bin/bash #匹配 查找/etc/passwd文件里用户名里有oo这两个字符的行 awk -F: '$1~"oo" {print $0}' /etc/passwd #不完全匹配 查找/etc/passwd文件里用户名不为root的行 awk -F: '$1!="root" {print $0}' /etc/passwd #不匹配 awk -F: '$1!~"oo" {print $0}' /etc/passwd