awk:把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理,是一个强大的文本分析工具,在对数据分析并生成报告时很有优势。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。
- 命令格式:
awk [options] 'program' FILE ... 其中: options: -F:指明输入时用到的字段分隔符,默认空白字符; -v:指定变量 -v var=value:自定义变量 program: PATTERN{ ACTION STATEMENTS;...} (语句之间用 ; 分隔) PATTERN--模式,这意味着并不是对文件中的每一行进行处理,而是处理那些能够被模式匹配到的行,不跟模式表示全文; ACTION--常见的处理机制是打印,命令有print和printf.
- 变量
- 常见内置变量
FS:切割时的分隔符,默认为空白字符; OFS:切割后输出时的分隔符,默认为空白字符; RS:输入时的换行符,默认为' '; ORS:输出时的换行符,默认为' '; NF:字段数量 ( $NF表示最后一个字段); NR:行数; FNR:分别计算各文件的行数; FILENAME:当前文件名; ARGC:命令行参数的个数; ARGV:数组,保存的是命令行所给定的各参数 示例: awk -v FS=: '{print $1}' /etc/passwd (相当于awk -F: "{print $1}" /etc/passwd) awk -v FS=: -v OFS=, '{print $1,$2}' /etc/passwd awk -v RS=' ' '{print}' /etc/issue awk '{print NF}' /etc/fstab (可对比 awk '{print $NF}' /etc/fstab,$NF表示最后一个字段) awk 'BEGIN{print ARGC}' /etc/fstab /etc/issue (命令行参数有3个,分别为awk,/etc/fstab,/etc/issue) awk 'BEGIN{print ARGV[#]}' /etc/fstab /etc/issue (#表示索引下标)
-
- 自定义变量
两种方式可自定义变量:
(1) -v var=value
(2) 在program中直接定义
示例:
awk -v test="hello gawk" 'BEGIN{print test}' (相当于awk 'BEGIN{test="hello gawk";print test}')
- 打印输出
print item1,item2,...
示例:
tail -5 /etc/fstab | awk '{print $2,$4}' ($1..$#:内置变量,表示分隔后的字段)
tail -5 /etc/fstab | awk '{print "hello:",$2,$4}' (注意,{}只能由单引号'' 引起来,不能用双引号"")
要点:
(1) 逗号,作为不同字段的分隔符;
(2) item可以是字符串,数值,当前记录的字段、变量或awk的表达式;
(3) 如省略item,相当于print $0(打印所有元素;$0表示所有字段)
-
- printf
格式化输出:printf FORMAT,item1,item2,...
释义:
(1) FORMAT必须给出;
(2) 不会自动换行,需要显式给出换行控制符,'
';
(3) FORMAT中需要分别为后面的每个item指定一个格式化符号;
格式符:
%c:显示字符的ASCII码;
%d,%i:显示十进制整数;
%e,%E:科学计数法数值显示;
%f:显示为浮点数;
%g,%G:以科学计数法或浮点形式显示数值;
%s:显示字符串;
%u:无符号整数;
%%:显示%自身
修饰符:
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度
示例:
%-3.1f,其中 '-' 表示左对齐;%+5.2d,其中 '+' 会显示数值的符号
awk -F: '{printf "Uername:%s, UID:%d
",$1,$3}' /etc/passwd
awk -F: '{printf "Uername:%-15s, UID:%d
",$1,$3}' /etc/passwd
- 操作符
算术操作符:x+y,x-y,x*y,x/y,x^y(多少次方),x%y 赋值操作符:=,+=,-=,*=,/=,%=,^=,++,-- 比较操作符:>,>=,<,<=,!=,== 模式匹配符:~:是否匹配;!~:是否不匹配 逻辑操作符:&&,||,! 函数调用:function_name(argu1,argu2,...) 条件表达式:selector ? if-true-expression : if-false-expression 示例: awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s : %-s ",$1,usertype}' /etc/passwd awk '!/^UUID/{print $0}' /etc/fstab awk -F: '$3>1000{print $1,$3}' /etc/passwd awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd
- PATTERN
(1) /regular expression/:仅处理被模式匹配到的行; (2) relational expression:关系表达式,为"真"时处理 ("真":结果是非0值或非空字符串); (3) line ranges:行范围 (startline, endline 或 /pat1/, /pat2/); 注意: 此处行范围不支持直接给出数字的格式 示例:
awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd
awk -F: '/^h/,/^s/{print $1}' /etc/passwd (4) BEGIN/END模式: BEGIN{}: 仅在开始处理文件中的文本之前执行一次; END{}:仅在文本处理完成之后执行一次 示例:
head /etc/passwd | awk -F: 'BEGIN{print "username uid"}{printf "%-12s%-5s ",$1,$3}END{printf "%10s ","END"}'
- 控制语句
- if-else
语法:
if(condition) statement [else statement]
使用场景:
对awk取得的整行或某个字段做条件判断.
示例:
awk -F: '{if($3>=1000) {printf "Common user: %s
",$1} else {printf "root or Sysuser: %s
",$1}}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
df -h | awk -F% '/^/dev/{print $1}' | awk '{if($NF>=20) print $1}'
-
- while and do while
语法:
while(condition) statement (条件"真"时进入循环;条件"假"时退出循环)
do statement while(condition) (至少执行一次循环体)
使用场景:
对一行内的多个字段逐一处理时使用;对数组中的各元素逐一处理时使用.
示例:
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg
-
- for
语法:
for(expr1;expr2;expr3) statement
for(variable assignment;condition;iteration process) {for-body}
示例:
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
特殊用法:遍历数组中的元素
语法:for(var in array) {for-body}
-
- switch
语法: switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}
-
- break and continue
break:终止循环
continue:中断本次循环继续下一轮
-
- next
提前结束对本行的处理而直接进入下一行; 示例: awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
- array数组
1) 关联数组: array[index-expression] index-expression: ① 可使用任意字符串;字符串要使用双引号; ② 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为"空串" 2) 若要判断数组中是否存在某元素,要使用"index in array"格式进行; 3) 若要遍历数组中的每个元素,要使用for循环:for(var in array) {for-body} 示例: awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}' netstat -tan | awk '/^tcp>/{state[$NF]++}END{for(i in state){ print i,state[i]}}' awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab netstat -tan | awk '/^tcp>/{state[$NF]++}END{for(i in state){print i,state[i]}}'
- 内置函数
rand():返回0和1之间一个随机数;
length([s]):返回指定字符串的长度;
sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容;
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容;
split(s,a,[r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中
示例:
netstat -tan | awk '/^tcp>/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
awk '/^[[:space:]]*kernel/{i=1;while(i<=NF){if(length($i)>=7)print $i,length($i);i++}}' /etc/grub.conf