awk
Linux使用到的文本处理工具
三者都支持正则表达式
grep文本过滤(性能比awk好的多)
sed 尤其适用在脚本中
awk 文本格式化工具,报告生成器,本身不编辑文本,工作与遍历模式下,迭代,每次读一行。
一、命令格式
awk [ options] ‘/pattern/{action}’
options
-F | 指定行分隔符 |
-v | 用户自定义变量 |
/pattern/
定界 | 指定的匹配范围,addr1,addr2 |
表达式 | 其值非0或为非空字符时满足条件,如:$1 ~ /foo/ 或 $1 == "magedu",用运算符~(匹配)和!~(不匹配) |
BEGIN{}: | 在遍历操作开始之前执行一次 |
END{} | 在遍历操作结束之后、命令退出之前执行一次 |
正则表达式 | |
空模式 | 匹配任意输入行 |
action
printf | |
Expressions: | 表达式 |
Control statements | 控制语句 |
Compound statements | 组合语句 |
Input statements | 输入语句 |
Output statements | 输出语句 |
二、awk编程语言
awk会对整个文件的每一行进行遍历扫描,对单一的行,我们还可以进行扫描循环
主要的要素
1.变量、数组
记录变量 | ||
$0 | 代表当前的行 | |
FS | field separator ,读取文件本时,所使用字段分隔符 eg:[root@localhost ~]# awk 'BEGIN{FS=":"}/root/{print $1,$NF}' /etc/passwd | |
RS | record separator,输入文本信息所使用的换行符;(读行的时候的行分隔符是什么) eg: | |
OFS | out filed separator 输出时以什么作为分隔符 eg:[root@localhost ~]# awk 'BEGIN{FS=":";OFS=":"}/root/{print $1,$NF}' /etc/passwd | |
ORS | output row separator(输出的时候行分隔符是什么) eg:[root@localhost ~]# awk 'BEGIN{FS=":";ORS="#"}{print $0}' /etc/passwd eg:[root@localhost ~]# awk 'BEGIN{RS=":";ORS="#"}{print $0}' /etc/passwd | |
数据变量 | ||
NR | the number of input records,awk命令所处理的记录数;如果有多个文件,这个数目会把处理的多个文件中行统一计数 (同时看多个文件,大家一起计行数) eg: # awk '{print NR,$0}' /etc/fstab /etc/issue | |
NF | Number of Field,当前记录的field个数 | |
FNR | 与NR不同的是,FNR用于记录正处理的行是当前这一文件中被总共处理的行数; ((同时看多个文件,各自计行数)) eg: # awk '{print FNR,$0}' /etc/fstab /etc/issue | |
ARGV | 数组,保存命令行本身这个字符串,如awk '{print $0}' a.txt b.txt这个命令中,ARGV[0]保存awk,ARGV[1]保存a.txt; eg: # awk -F: '/root/{print $1 ,"is a user in ", ARGV[1]}' /etc/passwd | |
ARGC | awk命令的参数的个数 eg: # awk 'BEGIN {print ARGC}' /etc/passwd /etc/issue | |
FILENAME | awk命令当前所处理的文件的名称 eg: # awk -F: '{print $0 ,"in",FILENAME}' /etc/fstab | |
ENVIRON | 当前shell环境变量及其值的关联数组 | |
用户自定义变量 | gawk允许用户自定义自己的变量以便在程序代码中使用,变量名命名规则与大多数编程语言相同,只能使用字母、数字和下划线,且不能以数字开头。gawk变量名称区分字符大小写。 -v eg: # awk -v num1=20 -v num2=30 'BEGIN{print num1+num2}' eg: # awk 'BEGIN{num1=10;num2=20;print num1+num2}' | |
在脚本中赋值变量 | 赋值变量在gawk中给变量赋值使用赋值语句进行,例如: awk 'BEGIN{var="variable testing";print var}' | |
在命令行中使用赋值变量 | gawk命令也可以在“脚本”外为变量赋值,并在脚本中进行引用。例如,上述的例子还可以改写为: awk -v var="variable testing" 'BEGIN{print var}' | |
注意: | $变量是显示字段的值,显示变量自身是不能加$ |
数组的使用
数组,数字索引 | 关联数组,字串索引 |
declear –a A A=([1]=’a’ [2]=’b’) echo ${A[1]} | decleare –A test test=([a]=’a’ [b]=’b’) echo ${test[a]} |
array[index-expression]
index-expression可以使用任意字符串;需要注意的是,如果某数据组元素事先不存在,那么在引用其时,awk会自动创建此元素并初始化为空串;因此,要判断某数据组中是否存在某元素,需要使用index in array的方式。
要遍历数组中的每一个元素,需要使用如下的特殊结构:
for (var in array) { statement1, ... } |
其中,var用于引用数组下标,而不是元素值;
netstat -ant | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 每出现一被/^tcp/模式匹配到的行,数组S[$NF]就加1,NF为当前匹配到的行的最后一个字段,此处用其值做为数组S的元素索引; awk '{counts[$1]++}; END {for(url in counts) print counts[url], url}' /var/log/httpd/access_log 用法与上一个例子相同,用于统计某日志文件中IP地的访问量
下面是非常实用的两个示例
# netstat -tn | awk '/^tcp/{tcp[$6]++}END{for(i in tcp) {printf "%20s %s\n",i,tcp[i]} }' #统计当前监听到的IP地址 # awk '! /^$/{access[$1]++}END{for(i in access) {printf "%20s %s\n",i,access[i]}}' /var/log/httpd/access_log #统计访问过系统的用户
删除数组变量
从关系数组中删除数组索引需要使用delete命令。使用格式为:
delete array[index]
2.控制语句
if-else | 语法:if (condition) {then-body} else {[ else-body ]} |
awk -F: '{if ($3 == 0) {print $1, "administrator";} else {print $1,"common user"}}' /etc/passwd 例子: awk -F: '{if ($1=="root") print $1, "Admin"; else print $1, "Common User"}' /etc/passwd awk -F: '{if ($1=="root") printf "%-15s: %s\n", $1,"Admin"; else printf "%-15s: %s\n", $1, "Common User"}' /etc/passwd awk -F: -v sum=0 '{if ($3>=500) sum++}END{print sum}' /etc/passwd | |
while | 语法: while (condition){statement1; statment2; ...} |
awk -F: '{i=1;while (i<=3) {print $i;i++}}' /etc/passwd awk -F: '{i=1;while (i<=NF) { if (length($i)>=4) {print $i}; i++ }}' /etc/passwd awk '{i=1;while (i<=NF) {if ($i>=100) print $i; i++}}' hello.txt | |
do-while | 至少执行一次循环体,不管条件满足与否 语法: do {statement1, statement2, ...} while (condition) |
awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd awk -F: '{i=4;do {print $i;i--}while(i>4)}' /etc/passwd | |
for | 语法: for ( variable assignment; condition; iteration process) { statement1, statement2, ...} |
awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd | |
for循环还可以用来遍历数组元素: 语法: for (i in array) {statement1, statement2, ...} | |
awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd | |
case(使用较少) | 语法:switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...} |
break 和 continue | 常用于循环或case语句中 |
next | 提前结束对本行文本的处理,并接着处理下一行;例如,下面的命令将显示其ID号为奇数的用户: |
# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd |
3.tor行,我们还可以进行内置函数
split | split(string, array [, fieldsep [, seps ] ]) 功能:将string表示的字符串以fieldsep为分隔符进行分隔,并将分隔后的结果保存至array为名的数组中;数组下标为从1开始的序列; |
# netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50 # netstat -tan | awk '/:80\>/{split($5,clients,":");ip[clients[4]]++}END{for(a in ip) print ip[a],a}' | sort -rn | head -50 # df -lh | awk '!/^File/{split($5,percent,"%");if(percent[1]>=20){print $1}}' | |
length | length([string]) 功能:返回string字符串中字符的个数; |
substr | substr(string, start [, length]) 功能:取string字符串中的子串,从start开始,取length个;start从1开始计数 |
system(command) | 功能:执行系统command并将结果返回至awk命令 |
systime() | 功能:取系统当前时间 |
tolower(s) | 功能:将s中的所有字母转为小写 |
toupper(s) | 功能:将s中的所有字母转为大写 |
实用示例
# df -lh | awk '!/^File/{split($5,space,"%");if(space[1]>=20){print $1}}' # awk '/GET/{split($7,s,"?");res[s[1]]++}END{ for(i in res) {printf "%23s,%s",i,res[i]}}' # df -lh | awk '!/^File/{split($5,space,"%");if(space[1]>=20){print $1}}' #统计磁盘使用状况 # awk '/GET/{split($7,s,"?");res[s[1]]++}END{ for(i in res) {printf "%23s,%s",i,res[i]}}' access_log #统计访问当前web服务器的资源
4.自定义函数
5.函数调用
function_name(para1,para2)
6.条件表达式
使用较少,这里仅给出知识点
selector?if-true-exp:if-false-exp if selector; then if-true-exp else if-false-exp fi a=3 b=4 eg:# awk -v a=2 -v b=3 'BEGIN{a>b?max=a:max=b;print max}' a>b?a is max:b ia max
7.操作符
算术操作符 | -x: 负值 |
+x: 转换为数值 | |
x^y: | |
x**y: 次方 | |
x*y: 乘法 | |
x/y:除法 | |
x/y:除法 | |
c | |
x-y | |
x%y | |
字符串操作符 | 只有一个,而且不用写出来,用于实现字符串连接 |
赋值操作符 | = |
+= | |
-= | |
*= | |
/= | |
%= | |
^= | |
**= | |
++ | |
-- | |
需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代; | |
布尔值 | awk中,任何非0值或非空字符串都为真,反之就为假 |
比较操作符 | x < y True if x is less than y. |
x <= y True if x is less than or equal to y. | |
x > y True if x is greater than y. | |
x >= y True if x is greater than or equal to y. | |
x == y True if x is equal to y. | |
x != y True if x is not equal to y. | |
x ~ y True if the string x matches the regexp denoted by y. | |
x !~ y True if the string x does not match the regexp denoted by y. | |
subscript in array True if the array array has an element with the subscript subscript. | |
表达式间的逻辑关系符: | && |
|| | |
三、输出
awk默认输出为空格
数据需要流式化
print item1, item2, ... | |
1、各项目之间使用逗号隔开,而输出时则以空白字符分隔 2、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出 3、print命令后面的item可以省略,此时其功能相当于print $0, 4、如果想输出空白行,则需要使用print "" | |
eg :awk 'BEGIN { print "line one\nline two\nline three" }' awk -F: '{ print $1, $3 }' /etc/passwd | |
printf | 默认不换行printf format, item1, item2, ... |
1、其与print命令的最大不同是,printf需要指定format; 2、format用于指定后面的每个item的输出格式; 3、printf语句不会自动打印换行符;\n | |
format格式的指示符都以%开头,后跟一个字符;如下 %c: 显示字符的ASCII码; %d, %i:十进制整数; %e, %E:科学计数法显示数值; %f: 显示浮点数; %g, %G: 以科学计数法的格式或浮点数的格式显示数值; %s: 显示字符串; %u: 无符号整数; %%: 显示%自身; | |
修饰符 N: 显示宽度; -: 左对齐; +:显示数值符号; | |
eg: # awk 'BEGIN{num1=10;printf "%d\n",num1}' # awk 'BEGIN{num1=10;num2=20;printf "%d %d\n",num1,num2}' # awk -F: '{printf "%-20s %s\n",$1,$NF}' /etc/passwd对齐方式,-20左对齐,20右对齐 |
输出重定向
基本格式
print items > output-file
print items >> output-file
print items | command
特殊文件描述符
/dev/stdin | 标准输入 |
/dev/sdtout | 标准输出 |
/dev/stderr | 错误输出 |
/dev/fd/N | 某特定文件描述符,如/dev/stdin就相当于/dev/fd/0 |
eg:# awk -F: '{printf "%-15s %i\n",$1,$3 > "/dev/stderr" }' /etc/passwd |
小结:
上述是在学习过程中,总结的笔记和一些示例,在后期的复习过程中会不断的新增内容,调整版面格式。这里,鼓励下自己。
Version: 1.1