AWK 编程实例
awk 并非简单的命令,而是一门专为文本处理设计的编程语言,诞生于20世纪70年代的贝尔实验室,名字取自三位创始人Alfred Aho、Peter Weinberger、Brian Kernighan的姓氏首字母。而Alfred Aho也是著名的编译原理(龙书)的作者。
在Linux世界里,文本处理是运维、开发绕不开的日常——从分析日志、提取配置信息到统计数据,都需要高效的工具支撑。而awk 凭借“按字段处理”的核心能力,成为了比grep(单纯匹配)、sed(整行编辑)更灵活的“文本处理瑞士军刀”。作为一款强大的文本分析语言,也是Linux和Unix环境中功能最强大的数据处理工具,没有之一。
awk 用法
awk '/pattern/ {action}'
| 变量名 | 含义 |
|---|---|
| ARGC | 命令行变元个数 |
| ARGV | 命令行变元数组 |
| FILENAME | 当前输入文件名 |
| FNR | 当前文件中的记录号 |
| FS | 输入列记录分隔符,默认为一个空格 |
| RS | 输入行记录分隔符,默认为\n |
| NF | 当前记录里列个数 |
| NR | 目前为止行记录数 |
| OFS | 输出列记录分隔符 |
| ORS | 输出行记录分隔符 |
awk 实例
显示文件file中包含101的匹配行
awk '/101/' file awk '/101/,/105/' file awk '$1 == 5' file awk '$1 == "CT"' file #注意必须带双引号 awk '$1 * $2 >100 ' file awk '$2 >5 && $2<=15' file
显示文件file的当前记录号、域数和每一行的第一个和最后一个域
awk '{print NR,NF,$1,$NF,}' file
awk '/101/ {print $1,$2 + 10}' file #显示文件file的匹配行的第一、二个域加10。
awk '/101/ {print $1$2}' file
awk '/101/ {print $1 $2}' file #显示文件file的匹配行的第一、二个域,但显示时域中间没有分隔符。
通过管道符获得输入,如:显示第4个域满足条件的行
df | awk '$4>1000000'
按照新的分隔符“|”进行操作
awk -F "|" '{print $1}' file
awk 'BEGIN {FS="[: \t|]"} {print $1,$2,$3}' file #通过设置输入分隔符(FS="[: \t|]")修改输入分隔符。
Sep="|"
awk -F $Sep '{print $1}' file #按照环境变量Sep的值做为分隔符。
awk -F '[ :\t|]' '{print $1}' file #按照正则表达式的值做为分隔符,这里代表空格、:、TAB、|同时做为分隔符。
awk -F '[][]' '{print $1}' file #按照正则表达式的值做为分隔符,这里代表[、]
通过文件awkfile依次进行控制
awk -f awkfile file
cat awkfile
/101/{print "\047 Hello! \047"} # 遇到匹配行以后打印 ' Hello! ' \047代表单引号。
{print $1,$2} # 因为没有模式控制,打印每一行的前两个域。
显示文件中第一个域匹配101的行
awk '$1 ~ /101/ {print $1}' file
awk '$1 * $2 >100 {print $1}' file
通过设置输出分隔符(OFS="%")修改输出格式
awk 'BEGIN { OFS="%"} {print $1,$2}' file
表达式1?表达式2:表达式3
if (表达式1)
表达式2
else
表达式3
BEGIN 表示在处理任意行之前进行的操作。
awk 'BEGIN { max=100 ;print "max=" max} {max=($1 >max ?$1:max); print $1,"Now max is "max}' file
取得文件第一个域的最大值
awk '{print ($1>4 ? "high "$1: "low "$1)}' file
匹配行后先将第3个域替换后再显示
awk '$1 == "Chi" {$3 = "China"; print}' file
awk '{$7 %= 3; print $7}' file # 将第7域被3除,并将余数赋给第7域再打印。
匹配行后为变量wage赋值并打印
awk '/tom/ {wage=$2+$3; printf wage}' file
END表示在所有输入行处理完后再执行
awk '/tom/ {count++;}
END {print "tom was found "count" times"}' file
gsub函数用于替换,再将结果输出到filename
awk 'gsub(/\$/,"");gsub(/,/,""); cost+=$4;
END {print "The total is $" cost>"filename"}' file
1 2 3 $1,200.00 1 2 3 $2,300.00 1 2 3 $4,000.00
通过if和else if完成条件语句
awk '{gsub(/\$/,"");gsub(/,/,"");
if ($4>1000&&$4<2000) c1+=$4;
else if ($4>2000&&$4<3000) c2+=$4;
else if ($4>3000&&$4<4000) c3+=$4;
else c4+=$4; }
END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file
通过exit在某条件时退出,但是仍执行END操作
awk '{gsub(/\$/,"");gsub(/,/,"");
if ($4>3000&&$4<4000) exit;
else c4+=$4; }
END {printf "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file
通过next在某条件时跳过该行,对下一行执行操作
awk '{gsub(/\$/,"");gsub(/,/,"");
if ($4>3000) next;
else c4+=$4; }
END {printf "c4=[%d]\n",c4}"' file
把file1、file2、file3的文件内容全部写到fileall中
格式为: 打印文件并前置文件名。
awk '{ print FILENAME,$0 }' file1 file2 file3 > fileall
把合并后的文件重新分拆为3个文件。并与原文件一致
awk ' $1!=previous { close(previous); previous=$1 }
{print substr($0,index($0," ") +1)>$1}' fileall
通过管道把date的结果送给getline给变量d,然后打印
awk 'BEGIN {"date"|getline d; print d}'
通过getline命令交互输入name,并显示出来
awk 'BEGIN {system("echo \"Input your name:\\c\"");
getline d;print "\nYour name is",d,"\b!\n"}'
打印/etc/passwd文件中用户名包含050x_的用户名
awk 'BEGIN {FS=":"; while(getline< "/etc/passwd" >0) { if($1~"050[0-9]_") print $1}}'
通过while语句实现循环
awk '{ i=1;while(i<NF) {print NF,$i;i++}}' file
通过for语句实现循环
awk '{ for(i=1;i<NF;i++) {print NF,$i}}' file
显示一个文件的全路径
type file|awk -F "/" '
{ for(i=1;i<NF;i++)
{ if(i==NF-1) { printf "%s",$i }
else { printf "%s/",$i } }}'
用for和if显示日期
awk 'BEGIN {
for(j=1;j<=12;j++)
{ flag=0;
printf "\n%d月份\n",j;
for(i=1;i<=31;i++)
{
if (j==2&&i>28) flag=1;
if ((j==4||j==6||j==9||j==11)&&i>30) flag=1;
if (flag==0) {printf "%02d%02d ",j,i}
}
}
}'
用awk统计5秒内的ContextSwitch Interrupt
vmstat -n 1 10|awk '/[0-9*]/ && $11 > 10 { cs_tal+=$12; int_tal+=$11; num++ } END{print "ContextSwitch: ",cs_tal/num, "Interrupt: ", int_tal/num}' # ContextSwitch: 15342 / 9 = 1704.67 Interrupt: 2151.33
统计端口总数并排序
#!/bin/sh sum=0 str="" for i in $(sed -n -e '/TunnelPorts/s:.*ports="\([^"]*\).*:\1,53,443,80,7193:' \ -e 's:,:\n:gp' a | sort -ru -n); do sum=$((sum+1)) str="$i,$str" done str=${str%,} echo "<TunnelPorts count=\"$sum\" ports=\"$str\" />"
利用iptraf统计出来的日志分析
iptraf -i eth1 -t 1 -B -L eth1.log
#!/bin/sh DEV=$1 PORT=$2 TIME=$3 [ -z $TIME ] && TIME=1 [ -z $DEV ] && echo "$0 ethx port time" && exit 0 IP=`ifconfig $DEV|grep "inet addr"|sed -r 's#.*addr:(.*) Bcast.*#\1#g'` [ ! -z $PORT ] && IP="$IP:$PORT" FILE="/tmp/$DEV.log" SORT="10" LIMIT="0" get_bad_ip() { awk '/\*\*\*/{next} { if($11~/^'"$NET"'/ && $13~/^'"$IP"'/) f="O_IN"; if($13!~/^'"$NET"'/) f="OUT"; if($11!~/^'"$NET"'/) f="IN"; gsub("(:[^ ]*)?(;)?",""); t=$6":"f" "$11; a[t]+=$8 } END { for(i in a) print i,a[i]}' $FILE | sort -k3nr | \ awk '{a[$1]++; if(a[$1]<'"$SORT"' && ($3-'"$LIMIT"')>0 ) \ b[$1]=b[$1]"\n"a[$1]" "$2" "$3" bytes" } END{ for(i in b) if(i!~/^:/) print i""b[i]"\n" }' } rm -rf /var/lock/iptraf/iptraf* rm -rf $FILE iptraf -i $DEV -t $TIME -B -L $FILE && renice -18 `pidof iptraf` sleep $TIME kill -9 `pidof iptraf` get_bad_ip
#!/bin/sh IP="10.4.1.1" NET="10.4.1" FILE="/var/log/iptraf/eth1.log" SORT="4" LIMIT="700" awk '/\*/{next} { if($11~/^'"$IP"'/ && $13~/^'"$NET"'/) f="O_OUT"; if($11~/^'"$NET"'/ && $13~/^'"$IP"'/) f="O_IN"; if($13!~/^'"$NET"'/) f="OUT"; if($11!~/^'"$NET"'/) f="IN"; gsub("(:[^ ]*)?(;)?",""); t=$6":"f" "$11; a[t]+=$8 } END { for(i in a) print i,a[i]}' $FILE | sort -k3nr | \ awk '{a[$1]++; if(a[$1]<'"$SORT"' && ($3-'"$LIMIT"')>0 ) \ b[$1]=b[$1]"\n"a[$1]" "$2" "$3" bytes" } END{ for(i in b) if(i!~/^:/) print i""b[i]"\n" }'
当列相同时合并行
-bash-3.00# cat /tmp/tcp_remote eth0 21 eth0 80 eth0 22 eth0 21 eth0 22 eth0 53 eth0 7193 eth0 80 eth0 443 eth0 9001 eth0 53 eth0 7193 eth0 80 eth0 443 eth0 21 eth0 9001
cat /tmp/tcp_remote |sort|uniq|awk '{a[$1]=a[$1](a[$1]?",":"@")$2}END{for (j in a) print j a[j]}'
大文件里高效率去重
awk '!seen[$0]++' text > sort.txt
统计当前系统的网络连接状态分类
netstat -antu | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' LAST_ACK 14 SYN_RECV 348 ESTABLISHED 70 FIN_WAIT1 229 FIN_WAIT2 30 CLOSING 33 TIME_WAIT 18122
| 状态 | 描述 |
|---|---|
| CLOSED | 无连接是活动的或正在进行 |
| LISTEN | 服务器在等待进入呼叫 |
| SYNRECV|一个连接请求已经到达,等待确认| |SYNSENT | 应用已经开始,打开一个连接 |
| ESTABLISHED | 正常数据传输状态 |
| FINWAIT1|应用说它已经完成| |FINWAIT2 | 另一边已同意释放 |
| ITMEDWAIT|等待所有分组死掉| |CLOSING|两边同时尝试关闭| |TIMEWAIT | 另一边已初始化一个释放 |
| LAST_ACK | 等待所有分组死掉 |
awk中调用系统外部命令
#!/bin/sh DEFINE="ESTABLISH#100 FIN_WAIT#100 TIME_WAIT#100 SYN_RECV#100" for STATE in ESTABLISH TIME_WAIT FIN_WAIT SYN_RECV;do NUM=0 echo ------------------ $STATE -------------------- netstat -antu|sed 's/::ffff://g'|grep $STATE|awk '{print $5}'|cut -d: -f1| \ awk '{++S[$1]} END{for(a in S) \ if( system("grep "a" /etc/whitelist.txt >/dev/null") != 0 && S[a]>'$NUM' ) print a"\t\tconnect num: "S[a]}' done for i in $DEFINE;do STATE=`echo $i|awk -F# '{print $1}'` NUM=`echo $i|awk -F# '{print $2}'` PORT=`echo $i|awk -F# '{print $3}'` if [ -z $PORT ];then netstat -antu|sed 's/::ffff://g'|grep $STATE|awk '{print $5}'|cut -d: -f1|awk '{++S[$1]} END{for(a in S) \ if( system("grep -q "a" /etc/whitelist.txt" ) != 0 && S[a]>'$NUM' ) print a" "S[a]" ""'$STATE'"" "'$NUM'}'| \ xargs -I[] /usr/sbin/record_ddos.sh [] # 调用另一个程序来分析数据并处理 else netstat -antu|sed 's/::ffff://g'|grep :$PORT|grep $STATE|awk '{print $5}'|cut -d: -f1|awk '{++S[$1]} END{for(a in S) \ if( system("grep -q "a" /etc/whitelist.txt" ) != 0 && S[a]>'$NUM' ) print a" "S[a]" ""'$STATE'"" "'$NUM'}'| \ xargs -I[] /usr/sbin/record_ddos.sh [] fi done # whitelist.txt是白名单
awk中调用bash中的变量方法
方法一: 双套单引号
TMP=1234 awk '{print "'$TMP'",$0}' urfile
方法二: 使用-v传递
TMP=1234 awk -v var=$TMP '{print var,$0}' urfile
awk读取前三行和倒数三行
awk -v N=$(wc -l < file.txt) 'NR<=3 || NR>=(N-2)' file.txt
awk做正则匹配字符截取
KEYS=$(sort -k1 /tmp/download.log | awk '/download/{print $1}' | \ uniq -c|awk '{if($1>3) {st=index($2,"key"); \ print substr($2,st+4,36)}}')
SED 单行脚本快速的 awk 实现
又拍流量排名统计脚本
awk '{split($7,a,"/");b[a[3]]+=$10} END{for(i in b) print i,b[i]}' /disk/ssd1/logs/b0.upaiyun.com.access.log|sort -k2nr |head -30 \ | awk '{print $1}'| nslookup| awk '/Non-authoritative answer/{getline;if($1~/upaiyun/) r=$1;else r=$NF; \ split(r,a,"."); rr=a[1]"."a[2]; print rr}' | sed -r 's@>@\n@g'|sort -u
