awk

作者: 六月天的安静 | 来源:发表于2017-07-17 22:22 被阅读51次

awk的简介和功能

  • awk: Linux 文本处理三剑客:grep、sed和awk。其中grep是一种文本过滤工具,sed是文本行编辑器,而 awk 是一种报表生成器,就是对文件进行格式化处理的,这里的格式化不是文件系统的格式化,而是对文件内容进行各种“排版”,进而格式化显示。

  • 在 linux 之上我们使用的是 GNU awk 简称 gawk ,并且 gawk 其实就是 awk 的链接文件,因此在系统上使用 awk和 gawk是一样的。我们通过 man gawk 的相关功能说明——gawk - pattern scanning and processing language (模式扫描及处理语言),gawk 是一种过程式编程语言。gawk 还支持条件判断、数组、循环等各种编程语言中 所有可以使用的功能,因此我们还可以把 gawk 称为一种脚本语言解释器

  • awk在处理文本时也是一次读取一行文本,然后根据输入分隔符(默认为空格符)进行切片,切成 n 个片段,然后将每一片都赋予 awk 内部的一个变量当中来进行保存,这些变量名为 $1、$2、$3......一直到最后一个,awk 就可以对这些片段单独处理,比如显示某一段、特定段,甚至可以对某些片段进行额外的加工处理,比如计数、运算等。

awk命令格式和选项

语法形式 :

  • awk [OPPTIONS] 'program' FILE1 FILE2 FILE3...
    其中 program :PARTTERN{action statement}

  • action statement : 动作语句,可以有多个语句组成,各语句间使用分号分割 ;

  • OPPTIONS :
    -F fs :fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:
    -v var=value : 赋值一个用户定义变量,将外部变量传递给awk
    -f scripfile : 从脚本文件中读取awk命令

输出命令print和printf

(1)print

  • 用法:print item1,item2,......
    • item : 字符串,用引号引用;print " readhat" , "linux"
    • 变量:显示变量的值 ,可以直接使用变量的名进行引用。print var_name
    • 数值:无需加引号
  • 要点:
    • 各 item 之间使用逗号分开;而输出时的分隔符为默认空白字符
    • 输出的各item可以为字符串或数值、当前记录的字段($#)、变量或awk的表达式;数值会被隐式转换为字符串进行输出
    • print后面的item省略时,相当于运行“$0”,用于输出整行;
    • 输出空白字符:print " "

(2)printf
语法:printf FORMAT item,item2,......
要点:

  • 必须提供FORMAT
  • 与print语句不同,printf不会自动换行,需要显示指定换行符:\n
  • FORMAT中需要分别为后面的每个item指定一个格式符,否则 item 无法显示:格式符都以%开头,后跟单个字符:
格式 描述
%d ,%i 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%e,%E 科学计数法显示数值
%x 以十六进制表示的整数
%o 以八进制表示的整数

awk 'BEGIN{n1=11.234;n2=12;n3=97;n4=16;printf("%.2f,%.3f,%x,%o\n",n1,n2,n3,n4);}

变量

(1)awk内置变量:

    $n  :当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。
    $0  :这个变量包含执行过程中当前行的文本内容。
    FS  :输入时字段分隔符(默认是任何空格)。
    RS  :记录分隔符(默认是一个换行符)。
    OFS :输出时字段分隔符(默认值是一个空格)。
    ORS :输出记录分隔符(默认值是一个换行符)
    NF  :表示字段数,在执行过程中对应于当前的字段数。
    NR 表示记录数,命令后跟的所有文件将统一合并基数
    FNR :行数,各文件单独计数
    FILENAME :当前正被awk读取的文件的文件名
    ARGC :awk命令行中的参数的个数
    ARGV :包含命令行参数的数组

例如:

1、打印 /etc/passwd 的第一列(用户名)和第三列(id号)信息
(1)用F指定分割符
awk -F: '{print $1,$3}' /etc/passwd
(2)输出时中间加个TAB键
awk -F: '{print $1,"\t",$3}' /etc/passwd

     (3)用内置变量指定分隔符
         `awk -v FS=: '{print $1,$3}' /etc/passwd`
     (4)用内置变量指定分隔符,并且在打印时再次引用此变量用于输出时字段之间的分隔符
        ` awk -v FS=: '{print $1,FS,$3}' /etc/passwd`
     (5)定义变量s,并且把此变量赋予内置变量FS
        ` s=:;awk -v FS=$s '{print $1,FS,$3}' /etc/passwd`
     (6)输出时给每个字段前面加上描述信息
         `awk -F: '{print "user: "$1,"id:"$3}' /etc/passwd`
     (7)指定输出时的分割符为“---”
         `awk -F: -v OFS='---' '{print $1,$3}' /etc/passwd    `  

2、读取每个分区的使用率
df | awk '/^\/dev\/sd/{print $5}'
df|grep "^/dev/sd"|awk '{print $5}'|awk -F% '{print $1}'

3、取 /etc/fstab 文件中的挂载点
cat /etc/fstab |grep "^UUID"|awk '{print $2}'

4、统计一下/etc/passwd/文件中每个记录(行)的字段数

(1)每行都会统计一下
awk -F: '{print NF}' /etc/passwd
(2)只在结束的时候统计一遍
awk -F: 'END{print NF}' /etc/passwd

5、统计某个文件的行数
awk 'END{print NR}' /etc/passwd
awk 'BEGIN{while(getline < "/etc/passwd" > 0) { i++ }; print i }'

6、统计/etc/passwd和/etc/fstab的行数,两文件合并统计
awk '{print NR,$0}' /etc/passwd /etc/fstab
awk 'END{print NR}' /etc/passwd /etc/fstab

9、统计/etc/passwd和/etc/fstab的行数,两文件单独统计
awk '{print FNR,$0}' /etc/passwd /etc/fstab

7、统计命令中的参数:
awk 'END{print ARGC}' /etc/passwd /etc/fstab

8、查看此命令行中的参数:
(1)查看此命令行中的第一个参数
awk 'END{print ARGV[0]}' /etc/passwd /etc/passwd
(2)查看此命令行中的第二个参数
awk 'END{print ARGV[1]}' /etc/passwd /etc/passwd
(3)查看此命令行中的第二个参数
awk 'END{print ARGV[2]}' /etc/passwd /etc/passwd

9、使用print $NF可以打印出一行中的最后一个字段,使用$(NF-1)则是打印倒数第二个字段,其他以此类推:
echo -e "line1 f2 f3\nline2 f4 f5"|awk '{print $NF}'
echo -e "line1 f2 f3\nline2 f4 f5"|awk '{print $(NF-1)}'

10、计算5个数相加:
seq 5|awk 'BEGIN{sum=0;print "相加"}{print $1"+";sum+=$1}END{print "等于";print sum}'

(2)自定义变量

将外部变量值传递给var

1、借助 -v 选项,可以将外部值(并非来自stdin)传递给awk:
name=aaa;echo|awk -v var=$name 'END{print var}'

另一种传递外部变量方法:

var1=aaa
var2=bbb
echo | awk '{print v1,v2 }' v1=$var1 v2=$var

当输入来自于文件时使用:

awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename

awk的运算与判断

作为一种程序设计语言所应具有的特点之一,awk支持多种运算,这些运算与C语言提供的基本相同。awk还提供了一系列内置的运算函数(如log、sqr、cos、sin等)和一些用于对字符串进行操作(运算)的函数(如length、substr等等)。这些函数的引用大大的提高了awk的运算功能。作为对条件转移指令的一部分,关系判断是每种程序设计语言都具备的功能,awk也不例外,awk中允许进行多种测试,作为样式匹配,还提供了模式匹配表达式 ~(匹配)~!(不匹配)。作为对测试的一种扩充,awk也支持用逻辑运算符。

Paste_Image.png

例:

awk 'BEGIN{a="2";print a++,++a}'

赋值运算符

逻辑运算符

例:

awk 'BEGIN{a=1;b=2;print(a>5 && b<=2),(a>5 || b<=2)}'

正则运算符

awk 'BEGIN{a="wangtest";if(a~/^wang*/){print "ok"}}'

关系运算符


例:

awk 'BEGIN{a=11;if(a>=9){print "ok"}}'

其它运算符


例子:

1、三目运算符(?):
awk 'BEGIN{a=1;b=2;{print a==b ? "ok" : "err"}}'
2、遍历数组
awk 'BEGIN{arr[1]="first";arr[2]="second";for(i in arr){print arr[i]}}'

流程控制语句

在 linux awk 的 while、do-while 和 for 语句中允许使用 break , continue 语句来控制流程走向,也允许使用exit这样的语句来退出。break中断当前正在执行的循环并跳到循环外执行下一条语句。if 是流程选择用法。awk中,流程控制语句,语法结构,与c语言类型。有了这些语句,其实很多shell程序都可以交给awk,而且性能是非常快的。下面是各个语句用法。

条件判断语句

if 语句:

if(表达式)
  语句1
else
语句2

格式中语句1可以是多个语句,为了方便判断和阅读,最好将多个语句用{}括起来。awk分枝结构允许嵌套,其格式为:

if(表达式)
  {语句1}
else if(表达式)
  {语句2}
else
 {语句3}

示例:

awk 'BEGIN{
test=65;
 if(test>90){
    print "优秀";
    } 
    else if(test>=60){
      print "良好";
    } 
  else{
    print "不及格";
   } 
 }' 

循环语句:

(1)while 语句

while(表达式)
    {语句}

示例:

awk 'BEGIN{
i=1;
test=100;
total=0;
while(i<=100){
   total+=i;
   i++;
 }
   print total;
 }'

(2)for 循环

for 循环有两种格式:
格式一:

for(变量 in 数组)
 {语句}

例子:

显示Socket所处不同样状态个数
ss -ant|awk '!/^S/{state[$1]++} END{for(i in state){print i,state[i]}}'

格式二:

for(变量;条件;表达式)
 {语句}

例子:

awk 'BEGIN{
total=0;
for(i=0;i<=100;i++){
total+=i;
}
print total;
}'

结果:5050

do循环

do
{语句} while(条件)

例子:

awk 'BEGIN{
i=1;
total=0;
do{total+=i;i++;}while(i<=100)
print total;
}'

结果:5050

其它语句:

break: 当 break 语句用于 while 或 for 语句时,导致退出程序循环。
continue: 当 continue 语句用于 while 或 for 语句时,使程序循环移动到下一个迭代。
next: next 能够导致读入下一个输入行,并返回到脚本的顶部。这可以避免对当前输入行执行其他的操作过程。
exit:exit 语句使主输入循环退出并将控制转移到END,如果END存在的话。如果没有定义END规则,或在END中应用exit语句,则终止脚本的执行。

数组的定义

数字做数组索引(下标):

Array[1]="sun"
Array[2]="kai"

字符串做数组索引(下标):

Array["first"]="www"
Array["last"]="name"
Array["birth"]="1994"

读取数组的值:

{ for(item in array) {print array[item]}; }
{ for(i=1;i<=len;i++) {print array[i]}; }

数组相关函数

  • 例:得到数组的长度:

awk 'BEGIN{info="it is a test";lens=split(info,array," ");print length(array),lens}'
结果:4 4
length返回字符串以及数组长度,split进行分割字符串为数组,也会返回分割得到数组长度。

  • asort对数组进行排序,返回数组长度。

awk 'BEGIN{info="it is a test";split(info,array," ");print asort(array)}'

  • 输出数组内容(无序,有序输出):
  • (1)无序输出:

awk 'BEGIN{info="it is a test";split(info,array," ");for(i in array){print i,array[i]}}'
结果:
4 test
1 it
2 is
3 a

  • (2)有序输出:

awk 'BEGIN{info="it is a test";lent=split(info,array," ");for(i=1;i<=lent;i++){print i,array[i]}}'
结果:
1 it
2 is
3 a
4 test
注意:数组下标是从1开始,与C数组不一样。

判断键值存在以及删除键值:

错误的判断方法:
awk 'BEGIN{arr["a"]="a1";arr["b"]="b1";if(arr["c"]!="1"){print "no found";};for(i in arr){print i,":"arr[i];}}'
结果:
no found
a a1
b b1
c
以上出现奇怪问题,arr[“c”]没有定义,但是循环时候,发现已经存在该键值,它的值为空,这里需要注意,awk数组是关联数组,只要通过数组引用它的key,就会自动创建改序列。

正确的判断方法:
awk 'BEGIN{arr["a"]="a1";arr["b"]="b1";if("c" in arr){print "OK"};for(i in arr){print i,":",arr[i]}}'
结果:
a : a1
b : b1
注意:if(key in array)通过这种方法判断数组中是否包含 key 键

删除键值:

awk 'BEGIN{arr["a"]="a1";arr["b"]="b1";delete arr["a"];for(i in arr){print i,":",arr[i]}}'
结果:
b : b1
注意:delete arr[key] 可以删除对应数组 key 的序列值。

二维、多维数组的使用

awk的多维数组在本质上实际上是一维数组,更确切一点,awk在存储上并不支持多维数组,awk提供了逻辑上模拟二维数组的访问方法。例如,array[2,4]=1这样的访问时允许的,awk使用一个特殊的字符串SUBSEP (\034)作为分割字段,在上面的例子中,关联数组array存储的键值实际上是2\034
类似一维数组的成员测试,多维数组可以使用if( (i,j) in array)这样,但是下表必须放在圆括号中。类似一维数组的循环访问,多维数组使用for(item in array)这样的语法遍历数组,与一维数组不同的是,多维数组必须使用split()函数来访问单独的下标。

例子:

awk 'BEGIN{
 for(i=1;i<=9;i++){
   for(j=1;j<=9;j++){
   arr[i,j]=i*j;print i,"*",j,"=",arr[i,j];
   }
  }
}' 

结果:
1*1 = 1
1*2 = 2
1*3 = 3

1 *4 = 4
1*5 = 5
..............
9* 3 = 27
9*4 = 36
9*5 = 45
9 *6 = 54
9*7 = 63
9*8 = 72
9*9 = 81
可以引用array[i,j]引用获得数组的内容

还可以用另外一种方法:

awk 'BEGIN{
for(i=1;i<=9;i++){
   for(j=1;j<=9;j++){ 
     arr[i,j]=i*j; 
   } 
  } 
for(m in arr){
  split(m,tarr,SUBSEP);print arr2[1],"*",arr2[2],"=",arr[m]; 
} 
}'

内置函数

awk 内置函数,主要分以下3中类型:算数函数、字符串函数、其它一般函数、时间函数。

算数函数:


例子:

awk 'BEGIN{OFMT="%.3f";a=sin(1);b=exp(10);c=log(10);d=int(3.1415);print a,b,c,d;}'  

OFMT 设置输出数据格式是保留3位小数。

获得随机数:

awk 'BEGIN{srand();n=int(100*rand());print n;}'

字符串函数

格式 描述
sub(Ere,Repl,[In]) 用Repl参数指定的字符串替换In参数指定的字符串中的由Ere参数指定的扩展正则表达式的第一个具体值。sub函数返回替换的数量。
gsub(Ere,Repl,[in]) 用Real参数指定的字符串替换In参数指定的字符串中的由Erect参数指定的扩展正则表达式的所有值,它和sub函数函数完全一样的执行
length(String) 返回String参数指定的字符串的长度(字符形式)。如果未给出String参数,则返回整个记录的长度($0记录变量)
blength(String) 返回String参数指定的字符串的长度(以字节为单位)。如果未给出String参数,则返回整个记录的长度($0记录变量)
substr(String,M,N) 返回具有N参数指定的字符数量子串。子串从String参数指定的字符串取得,其字符以M参数指定的位置开始取。M参数会将String参数中的第一个字符作为编号1。如果未指定N参数,则子串的长度将是M参数指定的位置到String参数的末尾的长度
split(String,A,[Ere]) 以Ere参数指定的分隔符(此分隔符可以通过Ere参数指定的扩展正则表达式进行,或用当前字段分隔符(FS特殊变量)来进行)去切割字符串String,并将切割后的结果保存至A表示的数组中
tolower(String) 返回String参数指定的字符串,字符串中每个大写字符将更改为小写
toupper(String) 返回String参数指定的字符串,字符串中每个小写字符将更改为大写
index(Streing1,String2) 在由String1参数指定的字符串中,返回位置,从1开始编号,如果String2参数不在String1参数出现,则返回0
match(String,Ere) 在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)

注:Ere 都可以是正则表达式

gsub,sub使用:

awk 'BEGIN{info="this is a test2017test!";gsub(/[0-9]+/,"!",info);print info}'
结果:
this is a test!test!

在 info 中查找满足正则表达式:/[0-9]+/"!"替换,并且替换后的值赋值给info,未给info赋值默认是$0

查找字符串(index使用)

awk 'BEGIN{info="this is a test2017test!";print index(info,"test")?"包含":"不包含"}'
结果:
包含

正则表达式匹配查找(match使用)

awk 'BEGIN{info="this is a test2017test!";print match(info,/[0-9]+/)?"OK":"no found";}'
结果:
OK

截取字符串(substr)使用

法一:awk 'BEGIN{info="this is a test2017test!";print substr(info,2,12)}'
法二:awk 'BEGIN{info="this is a test2017test!";a=substr(info,2,12);print a}'
结果:
his is a tes

从第4个字符开始,截取10个长度字符串

字符串分割(split使用)

awk 'BEGIN{info="this is a test";split(info,arr," ");print length(arr);for(i in arr){print i,arr[i];}}'
结果:
4
4 test
1 this
2 is
3 a

分割info,动态创建数组arr,这里比较有意思,awk for ...in 循环,是一个无序的循环。并不是从数组下标 1...n,因此使用时需要注意

一般函数


例子:

打开外部文件(close 用法)
awk 'BEGIN{while("cat /etc/passwd"|getline){print $0;}close("/etc/passwd")}'
结果:
root:x:0:0:root:/root:/bin/bash
system:x:0:0::/home/system:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
................................................

逐行读取外部文件(getline使用方法)
例1:awk 'BEGIN{while(getline < "/etc/passwd"){print $0;};close("/etc/passwd")}'
结果:
root:x:0:0:root:/root:/bin/bash
system:x:0:0::/home/system:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
…………………………………………

例2:awk 'BEGIN{print "Enter your name";getline name;print name}'
结果:
Enter your name
王健
王健

调用外部应用程序(system使用方法)
awk 'BEGIN{b=system("ls -al");print b;}'
结果:
total 102536
dr-xr-x---. 20 root root 4096 Jul 17 10:08 .
dr-xr-xr-x. 20 root root 267 Jul 14 22:44 ..
-rwx--x--x. 1 root root 1916 May 18 08:55 anaconda-ks.cfg
drwxr-xr-x. 2 root root 6 Jun 15 11:04 backup
...........

时间函数

建指定时间(mktime使用)
awk 'BEGIN{a=mktime("2017 07 17 13 28 04");print strftime("%c",a)}'
结果:
Mon 17 Jul 2017 01:28:04 PM CST

求2个时间段中间时间差
awk 'BEGIN{a=mktime("2017 07 17 13 20 00");b=mktime("2017 07 17 13 21 00");print b-a;}'
结果:
60

awk 'BEGIN{a=mktime("2017 07 17 13 48 00");b=systime();print b-a;}'
结果:
111

strftime日期和时间格式说明符:

相关文章

  • 18-文本处理三剑客之awk

    本章内容 ◆ awk介绍◆ awk基本用法◆ awk变量◆ awk格式化◆ awk操作符◆ awk条件判断◆ aw...

  • 2017 09-04 AWK

    本章主要学习内容awk介绍 awk基本用法 awk变量 awk格式化 awk操作符 awk条件判断 a...

  • 【技术案例】跟老男孩学运维-awk项目案例

    一个awk数组应用案例 [TOC] 0.技术点: awk awk数组 awk判断 awk数组赋值 awk函数spl...

  • awk

    awk:报告生成器,格式化文本输出 内容: awk介绍 awk基本用法 awk变量 awk格式化 awk操作符 a...

  • awk

    Linux System Environment awk功能 awk格式 awk 参数 一、awk截取列 二、显示...

  • 笔记-awk

    1.Awk基础介绍 2.awk语法格式 2.Awk工作原理 3.Awk内部变量 4.Awk格式输出 5.Awk模式...

  • awk用法详解

    awk 用法 awk ' pattern {action} ' 1、awk '/101/' file ...

  • Day64-shell编程_正则表达式_awk

    1.Awk基础介绍 2.Awk工作原理 3.Awk内部变量 4.Awk格式输出 5.Awk模式匹配 5.1符号 ...

  • linux-awk

    linux-awk awk基本结构 awk ‘BEGIN{ print “start”} pattern { co...

  • awk小说

    awk awk脚本的结构基本如下: awk ' BEGIN{ print "start" } patern { c...

网友评论

    本文标题:awk

    本文链接:https://www.haomeiwen.com/subject/uniqkxtx.html