AWK —— 三个作者的姓的首字母
awk很强大,是编程语言。
流编辑器、非交互式的文本编辑器、报告生成工具(对文本进行排版,数据汇总)
awk的版本
awk gawk nawk
旧版的 GNU awk 新版awk(Solaris)
# ll /usr/bin/awk
lrwxrwxrwx. 1 root root 14 May 23 17:03 /usr/bin/awk -> ../../bin/gawk
# ll /usr/bin/gawk
lrwxrwxrwx. 1 root root 14 May 23 17:03 /usr/bin/gawk -> ../../bin/gawk
# ll /bin/gawk
-rwxr-xr-x 1 root root 382456 Jul 4 2012 /bin/gawk
在我们的系统里,awk和gawk一样
工作模式:
1、行工作模式,每次读入一行,存到"$0"里
2、使用内置变量FS(区域分隔符,默认是空白)分隔这行,存到$1-$100
3、输出时使用内置变量OFS(输出字段分隔符,默认也是空白)
4、继续处理下一行
假设this is a test 这样的行
$0:this is a test
$1: this
$2: is
$3: a
$4: test
最多就到$100
一、awk的语法
awk [选项] '/正则表达式/{处理动作1;处理动作2;...;处理动作n}' 文件列表
二、例子
打印员工名字和生日
# awk '{print $1,$4}' employees
打印/etc/passwd文件的用户名和GID
# awk -F: '{print $1,$4}' /etc/passwd
三、基本操作
1、设定分隔符
-F:指定分隔符
分隔符可以是字母、符号、数字和"空"
可以同时指定多个分隔符
默认是空白(空格或者tab)
1)单个字符做分隔符
# awk -F: '{print $1}' awk.pass
2)复合分隔符,多个字符组成的分隔符
# awk -F ":/" '{print $1,$2}' awk.pass
3)同时指定多个分隔符
# awk -F "[:/]" '{print $1,$7}' awk.pass
2、awk的输出
1)print
打印的内容可以是文件的内容,也可以是和文件不相关的内容
# awk '{print "hello"}' awk.pass
\n:换行符
在每一行下面添加空行
# awk '{print $0"\n"}' awk.pass
# awk -F: '{print "user:"$1}' awk.pass //字符串输出要加双引号
\t:制表符,tab
# awk -F: '{print $1"\t"$2}' awk.pass
# awk -F: '/root/{print $1,$3}' awk.pass
练习:
ifconfig和awk结合,取出eth0的IP地址
# ifconfig eth0 | awk -F"[: ]" '/Bcast/{print $13}'
# ifconfig eth0| awk -F: '/Bcast/{print $2}' | awk '{print $1}'
2)printf —— 可以格式化输出,默认不输出换行符
printf format,item1,item2,...,itemn
format:指示符都以%开头,后面一个字符:
%s:表示字符串 string
%d:表示十进制整数,没有四舍五入,直接取整
%f:表示浮点数,即小数,默认小数点后保留六位,会四舍五入
%%: 表示%本身
%o: 八进制数
%x: 十六进制数
%c: 字符
修饰符:
N(数字),指定显示宽度 %2s
注意:宽度>=字段的最长的长度
-:表示左对齐(默认是右对齐) %-2s
+: 显示数值符号
# echo "12.35 11.2345 34.56" > awkf1
# awk '{printf "%d",$1}' awkf1
# awk '{printf "%d\n",$1}' awkf1
# awk '{printf "%f\n",$1}' awkf1
12.350000
# awk '{printf "%.1f\n",$1}' awkf1
12.3
# awk '{printf "%5.2f\n",$2}' awkf1
5.2表示显示宽度是5,即整数位和小数位的总显示长度为5;2表示保留两位小数
\n:换行符
printf与print的区别:
1)printf需要指定格式
2)printf默认不换行
3)printf默认没有输出分隔符
默认输出时,右对齐
# awk -F: '{printf "%10s %d\n",$1,$3}' awk.pass
左对齐
# awk -F: '{printf "%-10s %d\n",$1,$3}' awk.pass
练习:
/etc/passwd文件,让你格式化输出用户名,UID,GID三列
# awk -F: '{printf "%-13s\t%d\t%d\n",$1,$3,$4}' /etc/passwd
# awk -F: '{printf "%-15s %-10d %-10d \n",$1,$3,$4}' /etc/passwd
# awk -F: '{printf "%-15s %d %d\n",$1,$3,$4}' /etc/passwd
3、awk的操作符
1)算数运算符
+ - * /
% 取余 5%3 = 2
x^y x**y x的y次方,x的y次幂 2^5 = 32
# awk '{print $3+$5}' employees
# awk '{print $3-$5}' employees
# awk '{print $3*$5}' employees
# awk '{print $5/$3}' employees
# awk '{print $5%$3}' employees
2)关系运算符
(1)数值之间的关系
> < >= <= != ==
$3 == 0
$1 == "root"
(2)字符串之间的关系
x ~ /y/ —— 匹配正则
x !~ /y/ —— 不匹配
打印uid大于3的用户名和uid
# awk -F: '$3 > 3 {print $1,$3}' awk.pass
用户名包含a的,输出其名字
# awk -F: '$1 ~ /a/ {print $1}' awk.pass
3)逻辑运算符
&& || !
打印uid大于等于3,并且用户名中含有p的用户名和家目录
# awk -F: '$3 >= 3 && $1 ~ /p/ {print $1,$6}' /etc/passwd
打印用户名中不含p的行
# awk -F: '!($1 ~ /p/){print}' awk.pass
练习:/etc/passwd
1、打印GID在20和30之间的用户名和登录shell
# awk -F : '$4 >= 20 && $4 <= 30 {print $1,$7}' /etc/passwd
2、打印uid和gid不相等的用户名、uid和gid
# awk -F: '$3!=$4 {print $1,$3,$4 }' /etc/passwd
3、将uid和用户名互换位置输出,其他位置原样输出
# awk -F: '{print $3,$2,$1,$4,$5,$6,$7}' /etc/passwd
4、打印shell为非登录shell的用户名
# awk -F : '!($7 ~ /sh/) {print $1}' /etc/passwd
# awk -F: '$7 !~ /sh/{print $1}' /etc/passwd
5、打印所属组是bin的用户名
# awk -F: '$4==1 {print $1}' /etc/passwd
6、打印100以内能被7整除的数以及包含7的数(seq 100 生成1-100)
# seq 100 | awk '$1~/7/ || $1%7==0{print}'
-----------------------------------------------------------------
4)赋值运算符
= += -+ *= /= %= ++ -- ^= **=
4、定址(模式匹配)——awk+正则表达式
常见的模式:
1)没有模式,每一行都处理
# awk -F: '{print $1}' awk.pass
2)有一个模式,处理匹配到模式的行
# awk -F: '/^root/{print $1}' awk.pass
3)范围定址 //,// 注意:不能使用行号定址
# awk -F: '/^r/,/^d/{print $1}' awk.pass
范围定址仍然是最小匹配
5、awk的变量
内部变量、自定义变量
1)内部变量
(1)$0 表示一整行内容
# awk -F: '/root/{print $0}' awk.pass
# awk -F: '/root/{print}' awk.pass
# awk -F: '/root/' awk.pass
(2)$1 - $100
$1: 第一列
$2:第二列
......
$100:第100列
(3)与记录相关的变量 记录=行
FS:Field Separator 字段分隔符,默认是空白;其实是读取文本时,使用的字段分隔符 FS=":"
RS:Record Separator 记录分隔符,即行的分隔符,默认是换行符;其实就是读取文本时,使用的行分隔符 RS="/"
OFS:Output Field Separator 输出字段分隔符
ORS:Output Record Separator 输出记录分隔符
(4)与数据相关的变量
NR:记录数 awk处理的记录的总数,可以表示行号
NF:字段数 当前记录的字段数
$NF:当前行最后一个字段的值
$(NF-1):当前行的倒数第二个字段
打印/etc/passwd的第二行
# awk 'NR==2 {print}' /etc/passwd
打印偶数行
awk 'NR%2==0 {print}' /etc/passwd
打印/etc/passwd同时显示行号
# awk '{print NR,$0}' /etc/passwd
# awk -F: '{print NR": "NF}' awk.pass
# awk -F: '{print NR": "NF,$(NF-1)}' awk.pass
练习:
通过mount命令,查看所有分区是否可读可写,输出结果为:
/dev/sda2 rw
wp:# mount | awk -F "[ ()]" '/^\//{print $1,$7}'
2)自定义变量
用户根据自己需求去定义的变量
变量的命名规则:只能使用字母、数字、下划线,并且不能以数字开头,在awk中变量名是区分大小写的
合法的:a1 a abcd ab_123
不合法的:1a 1_a
什么是变量?
保存值的。在awk中变量可以先定义再使用,也可以直接就用。
说明:如果没有对变量进行定义没有初始值,如果变量被认为是字符串,它的值就是空"";如果字符串被认为是数值,那么它的值就是0。
给变量赋值:var=value
变量名=数字
变量名="字符串"
# awk '{a="qwe";print a}' awk.pass
练习:
自己定义两个变量,一个数值型,一个字符串型,使用printf打印它们
# awk '{a="Hello";b=100;printf"%s\t%d\n",a,b}' awk.pass
# awk '{a="hello";b=250;printf "%s %d\n",a,b}' awk.pass
赋值运算:
= += ......
a=5
a+=5 a=a+5
a-=5 a=a-5
a*=5 a=a*5
a++ a-- //先使用,后运算
++a --a //先运算,再使用
自增 自减
# awk '{a=5;b=a++;print a,b}' test
6 5
# awk '{a=5;b=a;a=a+1;print a,b}' test
6 5
# awk '{a=5;b=++a;print a,b}' a
6 6
# awk '{a=5;a=a+1;b=a;print a,b}' test
6 6
自己练习:
--a
# awk '{a=5;b=--a;print a,b}' test
4 4
# awk '{a=5;a=a-1;b=a;print a,b}' test
4 4
6、awk中的重定向 了解
> >>
# awk -F: '$3 > 2 {print $1,$3 > "/tmp/pass"} ' awk.pass
# cat /tmp/pass
adm 3
lp 4
# awk -F: '$3 > 2 {print $1,$3 >> "/tmp/pass"} ' awk.pass
# cat /tmp/pass
adm 3
lp 4
adm 3
lp 4
# awk -F: '$3 > 2 {print $1,$3 } ' awk.pass >> /tmp/pass
7、管道 了解
# awk -F: '{print $1,$3 | "sort -nr -k2"}' awk.pass
默认管道是不会关闭的,所以在脚本真正使用时候是需要关闭的
8、awk的特殊模式 BEGIN END
'
BEGIN{动作} //在处理文件之前执行的,并且只执行一次
/定址/{动作} //有可能会执行多次
END{动作} //在处理文件之后执行的,并且只执行一次
'
BEGIN:一般定义变量,初始化分隔符,定义数组,打印表头等等
END:一般END数据汇总,打印结尾
注意:BEGIN和END可以单独存在
# awk 'BEGIN{FS=":"}{print $1,$2}END{print "over"}' awk.pass
root x
bin x
daemon x
adm x
lp x
over
# awk 'BEGIN{print "class is not over"}' //只有BEGIN的时候,后面可以没有文件
class is not over
END:后面必须接文件,并且这个文件要么为空文件,要么为已存在的文件
# awk 'END{print "class is not over"}' awk.pass
class is not over
# touch abc
# awk 'END{print "class is not over"}' abc
class is not over
# awk 'BEGIN{sum=0}{sum+=$3;print $1,$3}END{print "sum: "sum}' employees
Tom 4424
Mary 5346
Sally 1654
Billy 1683
sum: 13107
练习:统计/etc目录下所有普通文件总大小
# ll /etc/ | awk '/^-/{sum+=$5}END{print sum}'
# ll /etc/ | awk 'BEGIN{sum=0}/^-/{sum+=$5}END{print sum}'
9、awk脚本
打印datafile文件的第一个字段和倒数第二个字段,对最后一个字段求和并打印
# cat sc.awk
BEGIN{ //注意:大括号一定要跟BEGIN同行
print "Class is over.That is not true."
}
{
printf "%-10s %d\n",$1,$(NF-1);sum+=$NF
}
END {
printf "sum:%10.2f\n",sum
}