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