Shell文本三剑客之awk

简介

awk是一个文本处理工具,通常用于处理数据并生成结果报告
awk的命名是它的创始人Alfred Aho、Peter Weinberger和Brian Kernighan姓氏的首个字母组成的

工作模式

类似sed,首先读取第一行,处理完后处理下一行,它可以在处理前面加一些数据,处理后面也可以加一些数据

语法格式

  1. 第一种形式:awk ‘BEGIN{}pattern{commands}END{}’ file_name

    BEGIN: 正式处理之前执行
    pattern: 匹配模式
    commands: 对匹配出来的数据的处理命令
    END: 处理完所有匹配数据后执行
    file_name: 文件名

  2. 第二种形式: standard output |awk ‘BEGIN{}pattern{commands}END{}’

    standard output表示对标准输出数据进行处理
    其他同上

awk的内置变量

内置变量 含义
$0 整行内容
$1-$n 当前行的第1-n个字段
NF 当前行的字段个数,也就是有多少列
NR 当前行的行号,从1开始计数
FNR 多文件处理时,每个文件行号单独计数,都是从0开始
FS 输入字段分隔符。不指定默认以空格或tab键分割
RS 输入行分隔符。默认回车换行\n
OFS 输出字段分隔符。默认为空格
ORS 输出行分隔符。默认为回车换行
FIL ENAME 当前输入的文件名字
ARGC 命令行参数个数
ARGV 命令行参数数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1. /etc/passwd 的所有内容显示
awk '{print $0}' /etc/passwd
2. 指定分隔符分割后的一列,/etc/passwd
awk 'BEGIN{FS=":"}{print $7}' /etc/passwd
3. 输出当前行某个文件分割后的数据的个数,NF(Number Field)
awk 'BEGIN{FS=":"}{print NF}' /etc/passwd
4. NR(Number Row),处理行号
awk 'BEGIN{FS=":"}{print NR}' /etc/passwd
当有多个文件时,直接加,如果是FNR(File Number Row)就会每个文件的行号从1开始
5. FS(Field Separator) 字段分隔符,不指定是以空格或tab键分割
awk 'BEGIN{FS=":"}{print $7}' /etc/passwd
6. RS(Row Separator) 行分割符,默认换行符
awk 'BEGIN{RS="---"}{print $0}' rs.txt
# rs.txt 内容aaaaaaaaaaa---bbbbbbbbbbbbbbbb---cccccccccccccc
7. OFS(Output Field Separator) 字段输出分隔符,默认空格
awk 'BEGIN{FS=":";OFS="##"}{print $1,$2}' /etc/passwd
# 如果$1 $2不以逗号分割OFS不起作用
8. ORS(Output Row Separator) 输出行分隔符,默认换行
awk 'BEGIN{ORS="-_+";FS=":"}{print $1,$2}' /etc/passwd
9. FILENAME 文件名
awk '{print FILENAME}' /etc/passwd
# 多少行数据,输出多少个
10. ARGC 变量个数
awk '{print ARGC}' /etc/passwd
# 2个参数, awk和/etc/passwd

printf输出

格式符 含义
%s 打印字符串
%d 打印十进制数
%f 打印一一个浮点数
%X 打印十六进制数
%o 打印八进制数
%e 打印数字的科学计数法形式
%c 打印单个字符的ASCII码
修饰符 含义
- 左对齐
+ 右对齐
# 显示8进制在前面加0 ,显示16进制在前面加0x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
格式符示例:
1、以字符串格式打印/etc/passwd中的第7个字段,以":"作 为分隔符
awk 'BEGIN{FS=":"}{printf "%s\n",$7}' /etc/passwd
2、以10进制格式打印/etc/passwd中的第3个字段,以":"作 为分隔符
awk 'BEGIN{FS=":"}{printf "%d\n",$3}' /etc/passwd
3、以浮点数格式打印/etc/passwd中的第3个字段,以":"作为分隔符
awk 'BEGIN{FS=":"}{printf "%f\n",$3}' /etc/passwd
4、以16进制数格式打印/etc/passwd中的第3个字段,以":"作 为分隔符
awk 'BEGIN{FS=":"}{printf "%x\n",$3}' /etc/passwd
5、以8进制数格式打印/etc/passwd中的第3个字段,以":"作为分隔符
awk 'BEGIN{FS=":"}{printf "%o\n",$3}' /etc/passwd
6、以科学计数法格式打印/etc/passwd中的第3个字段,以":"作为分隔符
awk 'BEGIN{FS=":"}{printf "%e\n",$3}' /etc/passwd
修饰符示例:
1、左对齐格式
awk 'BEGIN{FS=":"}{printf "%-20s %-20s\n",$1,$7}' /etc/passwd
2、右对齐格式
awk 'BEGIN{FS=":"}{printf "%20s %20s\n",$1,$7}' /etc/passwd
3、打印8进制或16进制数字是在前面加#
awk 'BEGIN{FS=":"}{printf "%#o\n",$3}' /etc/passwd
awk 'BEGIN{FS=":"}{printf "%#x\n",$3}' /etc/passwd

模式匹配

  1. RegExp(表示可以是一个敞亮字符串或者是一个正则表达式写法)
    1
    2
    3
    4
    匹配/etc/passwd文件行中含有root字符串的所有行
    awk 'BEGIN{}/^root/{print $0}' /etc/passwd
    匹配/etc/passwd文件行中以yarn开头的所有行
    awk 'BEGIN{}/^yarn/{print $0}' /etc/passwd
  2. 关系运算匹配
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    关系运算符匹配:
    < 小于
    > 大于
    <= 小于等于
    >= 大于等于
    == 等于
    != 不等于
    ~ 匹配正则表达式
    !~ 不匹配正则表达式

    不匹配正则表达式
    (1)、以:为分隔符, 匹配/etc/passwd文件中第3个字段小于50的所有行信息
    awk 'BEGIN{FS=":"}$3<50' /etc/passwd
    (2)、以:为分隔符,匹配/etc/passwd文件中 第3个字段大于50的所有行信息
    awk 'BEGIN{FS=":"}$3>50' /etc/passwd
    (3)、以:为分隔符,匹配/etc/passwd文 件中第7个字段为/bin/bash的所有行信息
    awk 'BEGIN{FS=":"}$7=="\/bin\/bash"' /etc/passwd
    (4)、以:为分隔符,匹配/etc/passwd文件中 第7个字段不为/bin/bash的所有行信息
    awk 'BEGIN{FS=":"}$7=="/bin/bash"' /etc/passwd
    (5)、以:为分隔符,匹配/etc/passwd中第3个字段包含3个以上数字的所有行
    awk 'BEGIN{FS=":"}$3~/[0,9]{3,}/' /etc/passwd
    布尔运算符匹配:
    || 或
    && 与
    ! 非

    (1)、以:为分隔符,匹配/etc/passwd文 件中包含root或mysql的所有行信息
    awk 'BEGIN{FS=":"}$1=="root" || $1=="mysql"' /etc/passwd
    (2)、以:为分隔符,匹配/etc/passwd文件中第3个字段小于50并且第4个字段大于50的所有行信息
    awk 'BEGIN{FS=":"}$3<50 && $4>50' /etc/passwd

动作表达式

运算符 含义
+
-
*
/
%
^或** 乘方
++x 在返回x变量之前, x变量加1
x++ 在返回变量之后,x变量加1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1、使用awk计算/etc/services中的空白行数量)
# 空白行的表示^$
awk '/^$/{sum++}END{print sum}' /etc/services
2、计算学生课程分数平均值,学生课程文件内容如下:
Allen 80 90 96 98
Mike 93 98 92 91
Zhang 78 76 87 92
Jerry 86 89 68 92
Han 85 95 75 90
Li 78 88 98 100
awk '{sum=$2+$3+$4+$5; AVG=sum/4; printf "%-8s%-8d%-8d%-8d%-8d%0.2f\n",$1,$2,$3,$4,$5,AVG}' stu.txt

3、使用awk输出/etc/passwd文件的行数。分两种方式显示行数,一种是正序如1.2.3.4..
awk '{line++; printf "%d:%s\n", line,$0}' /etc/passwd

awk条件语句

1
2
3
4
5
6
7
8
9
10
11
12
1、以:为分隔符,只打印/etc/passwd中第3个字段的数值在50-100范围内的行信息
awk 'BEGIN{FS=":"}{if(50<$3 && $3<100) print $0}' /etc/passwd


2、计算下列每个同学的平均分数,并且只打印平均分数大于90的同学姓名和分数信息
Allen 80 90 96 98
Mike 93 98 92 91
Zhang 78 76 87 92
Jerry 86 89 68 92
Han 85 95 75 90
Li 78 88 98 100
awk '{sum=$2+$3+$4+$5; AVG=sum/4; if(AVG>90)printf "%-8s%-8d%-8d%-8d%-8d%0.2f\n",$1,$2,$3,$4,$5,AVG}' stu.txt

awk循环语句

1
2
3
4
# 三种循环
awk 'BEGIN {i = 1; while (i < 6) { print i; ++i } }'
awk 'BEGIN { for (i = 1; i <= 5; ++i) print i }'
awk 'BEGIN {i = 1; do { print i; ++i } while (i < 6) }'

awk字符函数

函数名 解释 函数返回值
length(str) 计算字符串长度 整数长度值
index(str1 ,str2) 在str1中查找str2的位置 返回值为位置索引,从1计数
tolower(str) 转换为小写 转换后的小写字符串
toupper(str) 转换为大写 转换后的大写字符串
substr(str,m,n) 从str的m个字符开始,截取n位 截取后的子串
split(str,arr,fs) 按fs切割字符串,结果保存arr 切割后的子串的个数
match(str,RE) 在str中按照RE查找,返回位置 返回索引位置
sub(RE,RepStr,str) 在str中搜索符合RE的字串,将其替换为RepStr ;只替换第一个 替换的个数
gsub(RE,RepStr,str) 在str中搜索符合RE的字串,将其替换为RepStr;替换所有 替换的个数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1、以:为分隔符,返回/etc/passwd中每行中每个字段的长度
awk 'BEGIN{
FS=":"
}{
i=0;
while(i<=NF){
printf "%d", length($i);
if(i!=NF)printf ":";
i++;
}
printf "\n"

}' /etc/passwd
2、搜索字符串"I have a dream"中出现"ea"字符串的位置
awk 'BEGIN{str="I have a dream"; print index(str,"ea")}'
3、将字符串"Hadoop is a bigdata Framawork"全部转换为小写
awk 'BEGIN{str="Hadoop is a bigdata Framawork"; print tolower(str)}'
4、将字符串"Hadoop is a bigdata Framawork" 全部转换为大写
awk 'BEGIN{str="Hadoop is a bigdata Framawork"; print toupper(str)}'
5、将字符串"Hadoop Kafka Spark Storm HDFS YARN Zookeeper", 按照空格为分隔符
# 从1开始
awk 'BEGIN{str="Hadoop Kafka Spark Storm HDFS YARN Zookeeper"; split(str, arr, " "); print arr[1]}'
6、搜索字符串"Tranction 2345 Start:Select * from master"第一个数字出现的位置
awk 'BEGIN{str="Tranction 2345 Start:Select * from master"; print match(str, /[0-9]+/)}'

7、截取字符串" transaction start"的子串,截取条件从第4个字符开始,截取5位
awk 'BEGIN{str=" transaction start"; print substr(str, 4,5)}'
8、替换字符串"Tranction 243 start, Event ID:9002"中第一个匹配到的数字串为$符号
awk 'BEGIN{str="Tranction 243 start, Event ID:9002"; sub(/[0-9]+/, "$", str); print str}'

awk常用选项

选项 解释
-v 参数传递
-f 指定脚本文件
-F 指定分隔符
-V 查看awk的版本号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -v
[root@master ~]# num1=100
[root@master ~]# var="You"
[root@master ~]# awk -v num2=$num1 -v var1=$var 'BEGIN{print num2, var1}'
100 You

# 字符串有空格需要引号
[root@master ~]# var="Hello Shell"
[root@master ~]# awk -v num2=$num1 -v var1=$var 'BEGIN{print num2, var1}'
awk: fatal: cannot open file `BEGIN{print num2, var1}' for reading (No such file or directory)
[root@master ~]# awk -v num2=$num1 -v var1="$var" 'BEGIN{print num2, var1}'
100 Hello Shell

# -f awk后面的BEGIN放入test.awk后执行
[root@master datas]# awk -v num2=$num1 -v var1="$var" -f test.awk
100 Hello Shell

# -F
awk -F : 'BEGIN{}{print $1}' /etc/passwd

# -V
[root@master ~]# awk -V
GNU Awk 4.0.2

awk数组与Shell数组

awk数组与Shell数组的区别
前者从0开始,后者从1开始索引

awk数组

格式

1
2
3
4
array_name[index]=value
array_name:数组的名称
index:数组索引
value:数组中元素所赋予的值

操作

1
2
3
创建数组            array_name[index]=value
删除数组中的元素 delete array_name[index]
多维数组 array_name[rowIndex, colIndex]=value

应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 统计主机上所有的TCP连接状态数,按照每个TCP状态分类
netstat -an | grep tcp | awk '{array[$6]++}END{for(a in array) print a, array[a]}'
2. 计算横向数据总和,计算纵向数据总和
Allen 80 90 96 98
Mike 93 98 92 91
Zhang 78 76 87 92
Jerry 86 89 68 92
Han 85 95 75 90
Li 78 88 98 100
awk 'BEGIN{printf "%-10s%-10s%-10s%-10s%-10s%-10s\n", "Name","Chinese","English","Math","Physical","Total" }{
array["Chinese"]+=$2
array["English"]+=$3
array["Math"]+=$4
array["Physical"]+=$5
total = $2+$3+$4+$5
printf "%-10s%-10d%-10d%-10d%-10d%-10d\n", $1,$2,$3,$4,$5,total
}END{
printf "%-10s", ""
for(a in array) printf "%-10d",array[a]

printf "\n"
}' student.txt

Shell数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
array=("If" "we" "can" "only" "encounter" "each" "other" "rather" "than" "stay" "with" "each" "other,then" "I" "wish" "we" "had never encountered")

打印元素: echo ${array[2] }
打印元素个数: echo ${#array[@] }
打印元素长度: echo ${#array[3] }
给元素赋值: array[3]="Li"
删除元素: unset array[2] ;unset array
分片访问: echo ${array[@] :1:3}
元素内容替换: ${array[@]/e/E}只替换第-一个e;${array[@]//e/E}替换所有的e

数组的遍历:
for a in array
do
echo $a
done

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@ubuntu:~# array=("If" "we" "can" "only" "encounter" "each" "other" "rather" "than" "stay" "with" "each" "other,then" "I" "wish" "we" "had never encountered")
root@ubuntu:~# echo ${array[2]}
can
root@ubuntu:~# echo ${array[*]}
If we can only encounter each other rather than stay with each other,then I wish we had never encountered
root@ubuntu:~# echo ${#array[*]}
17
root@ubuntu:~# echo ${#array[@]}
17
root@ubuntu:~# echo ${array[@]}
If we can only encounter each other rather than stay with each other,then I wish we had never encountered
root@ubuntu:~# unset array[2]
root@ubuntu:~# echo ${array[@]}
If we only encounter each other rather than stay with each other,then I wish we had never encountered
root@ubuntu:~# echo ${array[@]:1:3}
we only encounter
root@ubuntu:~# echo ${array[@]/we/we and you}
If we and you only encounter each other rather than stay with each other,then I wish we and you had never encountered

awk小案例

利用awk处理日志,并生成结果报告

insert.sh生成数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/absh
#

function create_random(){
min=$1
max=$(($2-$min+1))
num=$(date+%s%N)
echo $(($num%$max+$min))
}
INDEX=1

while true
do
for user in A B C D E F G H I J K L
do
COUNT=$RANDOM
NUM1=`create_random 1 $COUNT`
NUM2=`expr $COUNT - $NUM1`
echo "`date '+%Y-%m-%d %H:%M:%S'` $INDEX Batches:$user INSERT $COUNT DATA INTO DATABASE:mydb table log, INSERT $NUM1 records successfully, failed $NUM2 records" >> 123.txt
INDEX=`expr $INDEX + 1`
done
done

需求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
(1)、统计每个人员分别插入了多少条record进数据库
awk 'BEGIN{
FS=" "
}{
array[substr($4, 9, 1)]++
}END{
for(a in array) print a, array[a]
}' 123.txt
(2)、统计每个人分别插入成功了多少record,失败了多少record
13 17
awk 'BEGIN{
FS=" "
}{
str=substr($4, 9, 1)
success[str]+=$13
fail[str]+=$17
}END{
for(a in success) print a, success[a],fail[a]
}' 123.txt
(3)、 将例子1和例子2结合起来,一起输出,输出每个人分别插入多少数据,多少成功,多少条失败
awk 'BEGIN{
FS=" "
printf "%-10s%-10s%-10s%-10s\n","Name","Number","Success","Failure"
}{
str=substr($4, 9, 1)
array[str]++
success[str]+=$13
fail[str]+=$17
}END{

for(a in success){
printf "%-10s%-10d%-10d%-10d\n",a, array[a],success[a],fail[a]
}
}' 123.txt
(4)、在例子3的基础上,加上结尾,统计全部插入记录数,成功记录数,失败记录数
awk 'BEGIN{
FS=" "
printf "%-10s%-10s%-10s%-10s\n","Name","Number","Success","Failure"
}{
str=substr($4, 9, 1)
array[str]++
success[str]+=$13
fail[str]+=$17
}END{

for(a in success){
Total+=array[a]
sTotal+=success[a]
fTotal+=fail[a]
printf "%-10s%-10d%-10d%-10d\n",a, array[a],success[a],fail[a]
}
printf "%-10s%-10d%-10d%-10d\n","TOTAL", Total,sTotal,fTotal
}' 123.txt
(5)、查找丢失数据的现象,也就是成功+失败的记录数,不等于一共插入的记录数。找出这些数据并显示行号和对应日志信息
修改123.txt文件的数据进行测试
awk '{if($6!=$13+$17) printf "%d:%s\n", NR,$0 }' 123.txt

Shell进行数据库操作

  • 学生数据库创建,定义在school.sql
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73

    DROP TABLE IF EXISTS `db_school`.`Student`;
    create table Student
    (
    Sno varchar(20),
    Sname varchar(50),
    primary key (Sno)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    DROP TABLE IF EXISTS `db_school`.`Course`;
    create table Course
    (
    Cno varchar(20),
    Cname varchar(50),
    Tno varchar(20),
    primary key (Cno)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    DROP TABLE IF EXISTS `db_school`.`SC`;
    create table SC
    (
    Sno varchar(20),
    Cno varchar(20),
    score int,
    primary key (Sno,Cno)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    DROP TABLE IF EXISTS `db_school`.`Teacher`;
    create table Teacher
    (
    Tno varchar(20),
    Tname varchar(50),
    primary key (Tno)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    INSERT INTO `Student`(Sno,Sname) VALUES ('001','陈一');
    INSERT INTO `Student`(Sno,Sname) VALUES ('002','郭二');
    INSERT INTO `Student`(Sno,Sname) VALUES ('003','张三');
    INSERT INTO `Student`(Sno,Sname) VALUES ('004','李四');
    INSERT INTO `Student`(Sno,Sname) VALUES ('005','王五');


    INSERT INTO `Teacher`(Tno,Tname) VALUES ('001','张老师');
    INSERT INTO `Teacher`(Tno,Tname) VALUES ('002','王老师');
    INSERT INTO `Teacher`(Tno,Tname) VALUES ('003','钱老师');
    INSERT INTO `Teacher`(Tno,Tname) VALUES ('004','刘老师');
    INSERT INTO `Teacher`(Tno,Tname) VALUES ('005','胡老师');


    INSERT INTO `Course`(Cno,Cname,Tno) VALUES ('001','语文','张老师');
    INSERT INTO `Course`(Cno,Cname,Tno) VALUES ('002','数学','王老师');
    INSERT INTO `Course`(Cno,Cname,Tno) VALUES ('003','英语','钱老师');
    INSERT INTO `Course`(Cno,Cname,Tno) VALUES ('004','物理','刘老师');
    INSERT INTO `Course`(Cno,Cname,Tno) VALUES ('005','政治','胡老师');


    INSERT INTO `SC`(Sno,Cno,score) VALUES ('001','001',50);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('001','002',60);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('001','003',70);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('001','004',80);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('001','005',90);

    INSERT INTO `SC`(Sno,Cno,score) VALUES ('002','001',90);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('002','002',80);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('002','003',70);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('002','004',60);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('002','005',50);

    INSERT INTO `SC`(Sno,Cno,score) VALUES ('003','001',81);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('003','002',82);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('003','003',83);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('003','004',84);
    INSERT INTO `SC`(Sno,Cno,score) VALUES ('003','005',85);
  • 引入数据库的命令
    1
    [root@node1 datasets]# mysql -uroot -proot school < school.sql
  • 在建立数据库之后可以先进行用户授权
    1
    2
    3
    4
    5
    grant all on school.* to dbuser@'%' identified by '123456'
    all 表示授权所有操作,增删改查
    on 后面的school.* 表示school数据库的所有表
    dbuser 表示授权用户名
    后面的@'%'表示授权所有网段, 如'192.168.37.%'

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×