目录
一、awk基本概念
awk 是一个按需求格式化文本再进行输出的工具。
和 sed 命令类似,awk 命令也是逐行扫描文件(从第 1 行到最后一行),寻找含有目标文本的行,如果匹配成功,则会在该行上执行用户想要的操作;反之,则不对行做任何处理。
awk -f 脚本文件 数据文件 执行awk脚本文件
awk 的应用场景:
看下 awk 能干些什么事情:
1. 能够将给定的文本内容,按照我们期望的格式输出显示,打印成报表。2. 分析处理系统日志,快速地分析挖掘我们关心的数据,并生成统计信息;3. 方便地用来统计数据,比如网站的访问量,访问的 IP 量等;4. 通过各种工具的组合,快速地汇总分析系统的运行信息,让你对系统的运行了如指掌;5. 强大的脚本语言表达能力,支持循环、条件、数组等语法,助你分析更加复杂的数据;awk 不是万能的,它比较擅长处理格式化的文本,比如 日志、 csv 格式数据等;
二、awk 命令语法
awk 基础语法:
awk
k –Fs ‘/pattern/ {action}’ input-file
(或者)
awk –Fs ‘{action}’ input-file
- -F 为字段分界符,也就是分隔符。如果不指定,默认会使用空格作为分界符。请注意,你也可以把分界符 用双引号引住,-F”:”也是正确的。
- /pattern/和{action}需要用单引号引起来。
- /pattern/是可选的。如果不指定,awk 将处理输入文件中的所有记录。如果指定一个模式,awk 则只处理匹配指定的模式的记录。
- {action} 为 awk 命令,可以是单个命令,也可以多个命令。整个 action(包括里面的所有命令)都必须放在{ }之间。
- Input-file 即为要处理的文件
举例
# awk -F: 'BEGIN{print"-----header-----"} {print $1,$7} END{print"-----footer-----"}' /etc/passwd
三、awk 程序结构(BEGIN,Body,END)区域
1. BEGIN 区域
Begin 区域的语法:
BEGIN { awk-commands }
BEGIN 区域的命令只最开始、在 awk 执行 body 区域命令之前执行一次。
- BEGIN 区域很适合用来打印报文头部信息,以及用来初始化变量。
- BEGIN 区域可以有一个或多个 awk 命令
- 关键字 BEGIN 必须要用大写
- BEGIN 区域是可选的
2. body 区域
body 区域的语法:
/pattern/ {action}
body 区域的命令每次从输入文件读取一行就会执行一次
- 如果输入文件有 10 行,那 body 区域的命令就会执行 10 次(每行执行一次)
- Body 区域没有用任何关键字表示,只有用正则模式和命令。
- Body 区域是可选的
3. END 区域
END 区域的语法:
END { awk-commands }
END 区域在 awk 执行完所有操作后执行,并且只执行一次。
- END 区域很适合打印报文结尾信息,以及做一些清理动作
- END 区域可以有一个或多个 awk 命令
- 关键字 END 必须要用大写
- END 区域是可选的
提示:如果命令很长,即可以放到单行执行,也可以用\折成多行执行。
四、awk 内置变量
FS 表示当前的列分隔符 RS 表示当前的行分隔符 OFS 输出字段分隔符 ORS 输出记录分隔符 NR 表示当前文件的行数 NF 表示当前文件有几列 FILENAME 当前处理的文件名 FNR 也是读取文件的行数,但是和NR 不同的是当读取的文件有两个或两个以上时,NR 读取完一个文件,行数继续增加 而FNR 重新从1开始记录
FS
用内置变量FS来表示分隔符,就要写在BEGIN区域里;
当遇到一个包含多个字段分隔符的文件时,不必担心,FS 可以搞定。你可以使用正则表达 式来指定多个字段分隔符,如 FS = “[,:%]” 指定字段分隔符可以是逗号 ,或者分号 : 或者百 分号 %。
创建文件:
#vi employee-multiple-fs.txt
101,John Doe:CEO%10000
102,Jason Smith:IT Manager%5000
103,Raj Reddy:Sysadmin%4500
104,Anand Ram:Developer%4500
105,Jane Miller:Sales Manager%3000
因此,下面的例子将打印 employee-multiple-fs.txt 文件中雇员名称和职位.
#awk 'BEGIN {FS="[,:%]"}{print $2,$3}' employee-multiple-fs.txt
John Doe CEO
Jason Smith IT Manager
Raj Reddy Sysadmin
Anand Ram Developer
Jane Miller Sales Manager
# awk 'BEGIN{FS=":"} {printf"%-20s %-2s %-2s %-2s\n", $1,$2,$3,$7}' /etc/passwd
RS
输入行分隔符,判断输入部分的行的起始位置,默认是换行符
awk 'BEGIN{RS=","}{print}' test4
这里说明一下,RS=“,”将以,为分割当作一行,即a,b a被当作一行,b也被当作一行,但是细心的会发现以“,”分割c和d之间是没有“,“的为什么也当作一行了呢,是因为输入中c后面还有一个换行符\n 即,输入应该是a,b,c\n只不过\n我们看不到,输入中,a一行,b一行,c\nd一行但是输出的时候系统会将\n视为换行符,所以看上去c和d是两行,实际上是一行
(转载于CSDN博主「昨天丶今天丶明天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41673534/article/details/80252016)
ORS
输出行分割符,默认的是换行符,它的机制和OFS机制一样,对输出格式有要求时,可以进行格式化输出;
# awk 'BEGIN{ORS=","}{print}' test4
全部的分割符,都变成了" ," ,具体的格式,可以根据实际需求,进行多种变化。
FILENAME
想要读取当前输入文件的名称;
# awk '{ print FILENAME }' /etc/passwd
(因为awk Body区域 的命令每读一行就会执行一次,所以会出现很多行)
OFS
来指定一个用于输出的字段分隔符,它会定义如何使用指定的字符分隔输出字段;在下面的例子中相当于先指定分隔符为 : ,再用 ==> 来替代它;
# awk -F':' ' BEGIN { OFS="==>" ;} { print $1, $4 ;}' /etc/passwd
NF
统计当前行的字段数;
# awk -F":" '{ print FILENAME,NR,NF }' /etc/passwd
没指定分隔符,那么默认分隔符为空格;
$NF与NF的区别
NF 表示的是浏览记录的域的个数
$NF 表示的最后一个Field(列),即输出最后一个字段的内容;举例:
以 : 为间隔符,输出文件的列数,又因为awk是每读取一行就执行一次命令,所以最终效果就是有很多行7。
NR
打印行号;
# awk -F: 'NR=="1" {print $1,$3,$7}' /etc/passwd (NR=="1" 表示打印第一行)
# awk -F: ' {print NR, $1,$3,$7}' /etc/passwd (在print后面输入NR,表示打印行号)
FNR
也是读取文件的行数,但是和NR 不同的是当读取的文件有两个或两个以上时,NR 读取完一个文件,行数继续增加 而FNR 重新从1开始记录
# awk '{print "NR:" NR "FNR:" FNR}' 1 2 3
五、过滤
==:
!=
>
<
>=
<=
$1~$N 代表每一个列的字段
$0 代表整行内容
~ 表示匹配
!~ 表示不匹配
/partrn/ 字符匹配
$0
~
六、awk输出格式
%d 整数
%s 字符串
%f 浮点数
\n 换行符
\t 制表符 tab
printf 格式化输出
%s字符串
%8s 表示输出8个字符的字符串, 不够8个字符右对齐。
%-8s 表示输出8个字符的字符串, 为左对齐。
以绝对宽度打印字符串
通过之前的例子可以知道,如果字符串长度超过指定的宽度,字符串仍然会整个被打印出来。# awk 'BEGIN { printf "%6s\n", "Good Boy!" }'
Good Boy!
如果要最多打印 6 个字符,要在指定宽度的数字前面加一个小数点,即使用”%.6s”代替”%6s”,
这样即使字符串比指定宽度长,也只打印字符串中的前 6 个字符。# awk 'BEGIN { printf "%.6s\n", "Good Boy!" }'
Good B
这个例子并非适用于所有版本的 awk,在 GAWK 3.1.5 上可以,但在 GAWK 3.1.7 上则不行。
当然,以绝对宽度打印字符串,最可取的方法是使用 substr 函数# awk 'BEGIN { printf "%6s\n", substr("Good Boy!",1,6) }' 【1,6表示从第一个字符开始一共输出6个字符】
Good B
控制精度
数字前面的点,用来指定其数值精度。
只过滤第一字段为root的行
注意!!==为等于的意思,=是赋值的意思,如果写成了=,那么结果如下所示:
# awk -F: '$1="root"{printf"%-20s %-2s %-2s %-2s\n", $1,$2,$3,$7}' /etc/passwd
&&符号
# awk -F: '$1!="root" && $3=="3" {printf"%-20s %-2s %-2s %-2s\n", $1,$2,$3,$7}' /etc/passwd
七、awk编程语句
条件语句
循环语句
数组
条件语句if
if(条件) command;else if(条件) command; else command
# awk -F':' '{if($1=="root") print "The user is ROOT" > "root.file"}' /etc/passwd (如果满足条件,就打印The user is ROOT到root.file文件中)
awk -F":" '{if($3<1000) print > "little.txt";else if($3>1000 && $3<1500) print > "middle.txt";else print > "big.txt"}' /etc/passwd
注意:在使用多个谈条件语句的时候,每写完一句。必须使用 ; 分隔开;
并且使用文件的时候,必须加上英文双引号;
使用且逻辑运算时要使用 &&;
做数据统计
# awk 'NR!=1 {sum+=sum+$5} END{print sum}' awk.file (除去awk.file的第一行,【默认分隔符是空格】将该文件的第五列相加,并且输出到标准输出)
变量
用数组统计第六列
# ps aux |awk 'NR!=1 {a[$1]+=$6} END{for( i in a) print i,a[i]}'
八、awk函数
length(s) 返回字符串s的长度
split() 分割字符串
tolower 该字段全部小写
toupper 该字段全部小写
length(s)
# awk -F':' '{if(length($1)>=4) print NR, "第一列数字段长度大于四的" > "root.file"}' /etc/passwd (将/etc/passwd文件中以 : 为分隔符的第一个字段,如果该字段长度>=4,那么输出该行的行号并且输出“第一列数字段长度大于四的”到root.file文件中)
toupper
# awk -F':' '{if(length($1)>=4) print NR,toupper($7)}' /etc/passwd
tolower
# awk -F' ' '{if(length($1)>=4) print NR,tolower($3)}' sed.file
九、printf 按照某一格式化输出文件
# vi score.file
HCL 100 110 119
hcl 100 159 516
# vi score.call
#! /bin/awk -f
BEGIN{
Chinese=0
Math=0
English=0
printf"NAME Chinese Math English Total\n"
printf"--------------------------------------------------\n"
}
{
Chinese+=$2
Math+=$3
English+=$4
printf"%-7s %-7d %-7d %-7d %-7d\n", $1,$2,$3,$4,$2+$3+$4
}
END{
print"---------------------------------------------------\n"
printf"Total: %-6d %-6d %-6d\n", Chinese,Math,English
printf"AGE: %7.2f,%7.2f,%7.2f\n",Chinese/NR,Math/NR,English/NR
}