述术LINUX学生会

awk

2017-07-17  本文已影响51人  六月天的安静

awk的简介和功能

awk命令格式和选项

语法形式 :

输出命令print和printf

(1)print

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

格式 描述
%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进行分割字符串为数组,也会返回分割得到数组长度。

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

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

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日期和时间格式说明符:

上一篇下一篇

猜你喜欢

热点阅读