企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 1. awk简介 awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。下面介绍的awk是以GUN的gawk为例的,在linux系统中已把awk链接到gawk,所以下面全部以awk进行介绍。 ## 2. awk命令格式和选项 ### 2.1 awk的语法有两种形式 ~~~ awk [options] 'script' var=value file(s) awk [options] -f scriptfile var=value file(s) ~~~ PATTERN:匹配模式 action:print, printf(可以自定义打印格式) ![](http://om4h63cja.bkt.clouddn.com/17-6-9/70670483.jpg) ### 2.2 命令选项 ~~~ -F fs or --field-separator fs 指定输入文件的分隔符,fs是一个字符串或者是一个正则表达式,如-F:、-F ab。 -v var=value or --asign var=value 赋值一个用户定义变量。 -f scripfile or --file scriptfile 从脚本文件中读取awk命令。 -W help or --help, -W usage or --usage 打印全部awk选项和每个选项的简短说明。 -W re-interval or --re-inerval 允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。 -W source program-text or --source program-text 使用program-text作为源代码,可与-f命令混用。 -W version or --version 打印bug报告信息的版本。 ~~~ ## 3. 模式和操作 ### 3.1 awk脚本是由模式和操作组成的 ~~~ pattern {action} 如$ awk '/root/' test,或$ awk '$3 < 100' test ~~~ 两者是可选的,如果没有模式,则action应用到全部记录,如果没有action,则输出匹配全部记录。默认情况下,每一个输入行都是一条记录,但用户可通过RS变量指定不同的分隔符进行分隔。 模式 ~~~ /正则表达式/:使用通配符的扩展集。 关系表达式:可以用下面运算符表中的关系运算符进行操作,可以是字符串或数字的比较,如$2>%1选择第二个字段比第一个字段长的行。 模式匹配表达式:用运算符~(匹配)和~!(不匹配)。 模式:指定一个行的范围。该语法不能包括BEGIN和END模式。 BEGIN:让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。 END:让用户在最后一条输入记录被读取之后发生的动作。 ~~~ ### 3.2 操作 操作由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内。主要有四部份: 变量或数组赋值 输出命令 内置函数 控制流命令 ## 4 awk变量 ### 4.1 awk内置变量之记录变量 ~~~ FS: field separator,读取文件本时,所使用字段分隔符;(-F),默认为空格 RS: Record separator,输入文本信息所使用的换行符; OFS: Output Filed Separator,输出文本时,所使用字段分隔符;默认为空格 ORS:Output Row Separator,输出文本时,所使用行分隔符; ~~~ ### 4.2 awk内置变量之数据变量 ~~~ NR: The number of input records,awk命令已经处理的记录数;如果有多个文件,这个数目会把处理的多个文件中行统一计数;相当于生成序号 FNR: 与NR不同的是,FNR用于记录正处理的行是当前这一文件中被总共处理的行数; NF:Number of Field,当前记录的field个数; ~~~ ~~~ awk '{print NF}' test.txt ;统计当前行的字段数 awk '{print $NF}' test.txt ;显示当前行最后一个字段 ~~~ ~~~ ARGV: 数组,保存命令行本身这个字符串,如awk '{print $0}' a.txt b.txt这个命令中,ARGV[0]保存awk,ARGV[1]保存a.txt; ARGC: awk命令的参数的个数; FILENAME: awk命令所处理的文件的名称; ENVIRON:当前shell环境变量及其值的关联数组; ~~~ ### 4.3 用户自定义变量 gawk允许用户自定义自己的变量以便在程序代码中使用,变量名命名规则与大多数编程语言相同,只能使用字母、数字和下划线,且不能以数字开头。gawk变量名称区分字符大小写。 * 在脚本中赋值变量 在gawk中给变量赋值使用赋值语句进行,例如: ~~~ awk 'BEGIN{var="variable testing";print var}' ~~~ * 在命令行中使用赋值变量 gawk命令也可以在“脚本”外为变量赋值,并在脚本中进行引用。例如,上述的例子还可以改写为: ~~~ awk -v var="variable testing" 'BEGIN{print var}' ~~~ ## 5 awk的操作符 ![](http://om4h63cja.bkt.clouddn.com/17-6-9/7984318.jpg) ## 6 匹配操作符(~) 用来在记录或者域内匹配正则表达式。如$ awk '$1 ~/^root/' test将显示test文件第一列中以root开头的行。 ## 7 比较表达式 `conditional expression1 ? expression2: expression3`,例如:`$ awk '{max = {$1 > $3} ? $1: $3: print max}' test`。如果第一个域大于第三个域,$1就赋值给max,否则$3就赋值给max。 ~~~ $ awk '$1 + $2 < 100' test。如果第一和第二个域相加大于100,则打印这些行。 $ awk '$1 > 5 && $2 < 10' test,如果第一个域大于5,并且第二个域小于10,则打印这些行。 ~~~ ## 8 print print的使用格式 print item1, item2, ... >[info] 各项目之间使用逗号隔开,而输出时则以空白字符分隔; 输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出; print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print ""; ~~~ awk 'BEGIN { print "line one\nline two\nline three" }' awk -F: '{ print $1, $3 }' /etc/passwd awk 'BEGIN {FS=":";OFS="@"} { print $1, $3 }' /etc/passwd ~~~ ## 9 printf printf命令的使用格式 printf format, item1, item2, ... 要点: >[info]其与print命令的最大不同是,printf需要指定format; format用于指定后面的每个item的输出格式; printf语句不会自动打印换行符;\n ### 9.1 format 格式的指示符都以%开头,后跟一个字符;如下: ~~~ %c: 显示字符的ASCII码; %d, %i:十进制整数;int %e, %E:科学计数法显示数值; %f: 显示浮点数; %g, %G: 以科学计数法的格式或浮点数的格式显示数值; %s: 显示字符串;string %u: 无符号整数; %%: 显示%自身; ~~~ ### 9.2 修饰符 ~~~ N: 显示宽度; -: 左对齐; +:显示数值符号; ~~~ ### 例子 ~~~ awk -F: '{printf "%-15s %d\n",$1,$3}' /etc/passwd awk -F: 'BEGIN{printf "%-15s%-10s\n","USER","ID"}{printf "%-15s%-10i\n",$1,$3}' /etc/passwd awk -F: 'BEGIN { printf "%-15s %-15s\n","user","uid";print "----------------------"} { printf "%-15s %-15d\n",$1,$3} END { print "----------------------" }' /etc/passwd ~~~ ## 10 awk编程 ### if-else ~~~ if (condition) {then-body} else {[ else-body ]} ~~~ ~~~ awk -F: '{if ($1=="root") printf "%-15s: %s\n", $1,"Admin"; else printf "%-15s: %s\n", $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: '{if($NF ~"bash") {printf "%-10s%s\n",$1,"admin"} else {printf "%-10s%s",$1,"commo\n"}}' /etc/passwd awk -F: -v sum=0 '{if ($3>=500) sum++}END{print sum}' /etc/passwd ~~~ ### while 对字段进行循环时使用,比如,判断字段长度大于4的给予显示 ~~~ 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 ~~~ ### do-while ~~~ do {statement1, statement2, ...} while (condition) ~~~ ~~~ awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /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, ...} ~~~ ### 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 ~~~ ### awk中使用数组 array[index-expression] 要遍历数组中的每一个元素,需要使用如下的特殊结构: for (var in array) { statement1, ... } 其中,var用于引用数组下标,而不是元素值; 例子 ~~~ # awk -F: '$NF~/[^[:space:]]/{shell[$NF]++}END{for(A in shell){print A,shell[A]}}' /etc/passwd /bin/sync 1 /bin/bash 2 /sbin/nologin 33 /sbin/halt 1 /sbin/shutdown 1 ~~~ ~~~ 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(ip in counts) print counts[ip], ip}' /var/log/httpd/access_log 用法与上一个例子相同,用于统计某日志文件中IP地的访问量 ~~~ ### awk的内置函数 split(string, array [, fieldsep [, seps ] ]) 功能:将string表示的字符串以fieldsep为分隔符进行分隔,并将分隔后的结果保存至array为名的数组中;数组下标为从0开始的序列; ~~~ netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50 ~~~ length([string]) 功能:返回string字符串中字符的个数; substr(string, start [, length]) 功能:取string字符串中的子串,从start开始,取length个;start从1开始计数; system(command) 功能:执行系统command并将结果返回至awk命令 systime() 功能:取系统当前时间 tolower(s) 功能:将s中的所有字母转为小写 toupper(s) 功能:将s中的所有字母转为大写 ## 常用例子 ### 一、文本间隔 1、在每一行后面增加一空行 ~~~ sed G guo.sh awk '{printf("%s\n\n",$0 ) }' awk '{print $0, "\n"}' ~~~ 2、将文件中原来的空行删掉,并在在每一行后边增加一空行 ~~~ sed '/^$/d;G ' awk '!/^$/ {printf("%s\n\n",$0 ) }' ~~~ 3、在匹配式样的行前插入一空行 ~~~ sed '/good/i\\' sed '/hello/{x;p;x;}' awk '{ if(/hello/) printf("\n\%s\n",$0);else print $0}' ~~~ 4、在匹配式样的行后插入一空行 ~~~ sed '/good/a\\' sed '/hello/G' awk '{if(/hello/) printf("%s\n\n",$0) ;else print $0}' ~~~ 5、在匹配式样的行前、行后各插入一空行 ~~~ sed '/hello/{x;p;x;G;}' awk '{ if(/hello/) printf("\n\%s\n\n",$0);else print $0}' ~~~ ### 二、文本的替换 1、在每一行查找到good,然后把good替换为bad ~~~ sed 's/good/bad/'只把每行的第一个good替换为bad sed 's/good/bad/2'只把每行的第二个good替换为bad sed 's/good/bad/g'把每一行的所有good替换为bad sed 's/.∗good/\1bad/'只把每一行的的最后一个good替换位bad sed 's/.∗good.∗good/\1bad\2/'只把每一行的的倒数第二个good替换位bad ~~~ ~~~ awk '{sub(/good/,"bad"); print $0}'只把每行的第一个good替换为bad awk '{gsub(/good/,"bad"); print $0}'把每一行的所有good替换为bad sed 's/root/good/p' test awk '{gsub(/root/,"good");print $0}' test awk '{gsub(/root/,"god");print $0}' test ~~~ 2、只在出现字符串fell字符串的前提下,将找到的行中的good替换为bad ~~~ sed '/fell/ s/good/bad/g' awk '{if(/fell/) gsub(/good/,"bad"); print $0 }' ~~~ 3、只在不出现字符串fell字符串的前提下,将找到的行中的good替换为bad ~~~ sed '/fell/ !s/good/bad/g' awk '{if(!/fell/) gsub(/good/,"bad"); print $0 }' ~~~ 4、多单词替换,替换为一个单词 ~~~ sed 's/good/bad/g;s/fell/bad/g;s/sun/bad/g' sed 's/good\|fell\|sun/bad/g' awk '{gsub(/good|fell|sun/,"bad") ; print $0}' ~~~ 5、倒置所有行,第一行变为最后一行(模拟tac) ~~~ sed '1!G;h;$!d' sed -n '1!G;h;$p' awk '{A[i++]=$0} END{for (j=i-1;j>=0;j--) print A[j]}' ~~~ 6、将每两行连接为一行 ~~~ sed '$!N;s/\n/ /' awk '{f=!f;if(!f) printf("%s",$0);else printf("%s\n",$0)}' ~~~ 7、在文件中每隔5行显示一空行 ~~~ sed '0~5G' sed 'n;n;n;n;G' awk '{print $0 ;i++;if(i==5) {printf( "\n") ;i=0}}' ~~~ #### 三、选择性的显示特定行 1、显示文件的前10行 ~~~ sed 10q awk '{print ;if(NR==10) exit }' ~~~ 2、显示文件的第一行 ~~~ sed q awk '{ print;exit}' ~~~ 如何用sed打印文件范围从第二行至倒数第二行? sed '1d;$d' 1、求和 ~~~ cat data|awk '{sum+=$1} END {print "Sum = ", sum}' ~~~ 2、求平均 ~~~ cat data|awk '{sum+=$1} END {print "Average = ", sum/NR}' ~~~ 3、求最大值 ~~~ cat data|awk 'BEGIN {max = 0} {if ($1+0 > max+0) max=$1 fi} END {print "Max=", max}' ~~~ 4、求最小值(min的初始值设置一个超大数即可) ~~~ awk 'BEGIN {min = 1999999} {if ($1<min) min=$1 fi} END {print "Min=", min}' ~~~ 5、求访问次数的Top 10 Resource,可以根据此进行优化 ~~~ cat output/logs/cookie_logs/`date +%u`/cookie_log|grep -v '172.16'|grep -v '127.0.0.1' |awk -F' ' '{ if(index($1,"219.141.246")!=0) print $2; else print $1 } '|sort|uniq -c|sort -n |tail -n 10 ~~~ 连接状态 ~~~ netstat -lnta | awk '{stat[$6]++} END {for (i in stat){print i,stat[i]}}' ~~~ 查看日志 ~~~ awk '{a[$1]+=1} END {for(i in a){print a[i], i}}' log | sort -rn ~~~