43.linux三剑客之awk

1.awk1:

1.awk基础
	vawk [options] 'Pattern{Action}' file # 语法格式
    echo ddd >> testd
    awk '{print}' testd
    awk '{print $5}' testd   #代表输出第五列的参数,没有指定分隔符默认使用空格,-F制定分隔符
	awk 'print $1,$2' test   #输出第一列和第二列,逗号表示输出内容使用空格分开
2.awk模式
	AWK 包含两种特殊的模式:BEGIN 和 END
	BEGIN 模式指定了处理文本之前需要执行的操作:
	END 模式指定了处理完所有行之后所需要执行的操作:
    awk 'BEGIN{print "aaa", "bbb"}{print $1}END{"game over"}' # 表示在开始输出aaa bbb ,结束之后输出gameover,中间的就是处理的行
    作用:可以用来添加表头和标题
3.awk分隔符
	awk有哪些分隔符,awk的默认分割符是空格,但是,这样描述并不精确,因为,awk的分隔符还分为两种,"输入分隔符 FS" 和 "输出分隔符OFS"
    # 输入
    awk -F # '{print $1,$2}' test  # 制定以#输入分割
    awk -v FS='#' '{print $1,$2}' test  # 制定以#输入分割
    # 输出
    awk -v OFS='+++' '{print $1,$2}' test  # 制定以+++输出分割
    
    awk '{print $1$2}' test  # 不使用分隔符
    awk '{print $1 $2}' 表示每行分割后,将第一列(第一个字段)和第二列(第二个字段)连接在一起输出。

awk '{print $1,$2}' 表示每行分割后,将第一列(第一个字段)和第二列(第二个字段)以输出分隔符隔开后显示。
4.变量
	FS:输入字段分隔符, 默认为空白字符
    awk -v FS='#' '{print $1,$2}' test  # 制定以#输入分割
    
    OFS:输出字段分隔符, 默认为空白字符
    awk -v OFS='+++' '{print $1,$2}' test  # 制定以+++输出分割
    
    RS:输入记录分隔符(输入换行符), 指定输入时的换行符
     我们不想以默认的"回车换行"作为"行分隔符",而是想使用空格作为所谓的行分隔符,也就是说,我们想让awk认为,每遇到一个空格,就换行,换句话说,我们想让awk以为每次遇到一个空格就是新的一行
    akw -v RS=' ' '{print NR,$0}'
    
    ORS:输出记录分隔符(输出换行符),输出时用指定符号代替换行符
    让awk认为,"+++"才是真正的输出行分隔符
    awk -v ORS='+++' '{print NF,$0}' test1
    awk -v ORS='+++' -v RS='' '{print NF,$0}' test1
    
    NF:number of Field,当前行的字段的个数(即当前行被分割成了几列),字段数量
    awk -v ORS='+++' -v RS='' '{print NF,$0}' test1
    3 1 2 3
    
    NR:行号,当前处理的文本行的行号。
    awk '{print NR,NF}' test  # 输出每一行的行号和列数
    
    FNR:各文件分别计数的行号
    awk '{print FNR,$0}' test test1  # 分别对每个文件的行数进行计数
    
    FILENAME:当前文件名
    awk '{print FILENAME,FNR,$0}' test test1
    
    ARGC:命令行参数的个数
    awk 'BEGIN{print "aaa",ARAV[0],ARGV[1],ARGV[2],ARGC}' test2 test1 # ARAV[0]表示awk本身,ARGC表示数组的长度
    aaa awk test1 test2 3 
    
    ARGV:数组,保存的是命令行所给定的各参数
    awk 'BEGIN{print "aaa",ARGV[1] }' test1 test2
    aaa test1
	awk 'BEGIN{print "aaa",ARGV[1],ARGV[2]}' test2 test1
    aaa test2 test1
    awk 'BEGIN{print "aaa",ARAV[0],ARGV[1],ARGV[2]}' test2 test1 # ARAV[0]表示awk本身
    aaa awk test1 test2

5.自定义变量
	方式一:
    abc="6666"
    awk -v myvar=$abc 'BEGIN{print myvar}'
    666
    awk -v myvar="testvar" 'BEGIN{print myvar}' 
    testvar
    方式二:
    awk -v 'BEGIN{myvar="testvar"; myvar2="testvar2";print myvar}'
    testvar    testvar2
    
6.awk格式化
	awk 'BEGIN{print $1}' test1
    abc
    8yy
    awk 'BEGIN{printf $1}' test1
    abc 8yy
    awk 'BEGIN{printf "%s\n" , $1}' test1  # 需要使用逗号隔开
    abc 
    8yy
    printf "%s\n" teststring
    teststring
    printf "%s\n" 1 2 3 4 5
    1
    2
    3
    4
    5
    
    awk 'BEGIN{printf "%s\n" , 1,2,3,4,5}' 
    1
    awk 'BEGIN{printf "%s\n" , $1}' 
    awk 'BEGIN{printf "%s\n%s\n" , 1,2,3,4,5}'  # 必须与传入的参数数量相同
    1
    2
    总结:
    1)使用printf动作输出的文本不会换行,如果需要换行,可以在对应的"格式替换符"后加入"\n"进行转义。
    2)使用printf动作时,"指定的格式" 与 "被格式化的文本" 之间,需要用"逗号"隔开。
    3)使用printf动作时,"格式"中的"格式替换符"必须与 "被格式化的文本" 一一对应。
    
    cat test1
    abc 123 iuy ddd
    awk 'printf "第一列: %s 第二列: %s\n" , $1,$2 ' test1
    第一列: abc 第二列:123
    
    cat test1
    abc#123#iuy#ddd
    awk -v FS='#' 'printf "第一列: %s 第二列: %s\n" , $1,$2 ' test1
    第一列: abc 第二列:123
    
    awk -v FS='[ ]+' 'BEGIN{printf "%-10s\t %s\n" , "用户名", "用户ID"}{printf "%-10s\t \%\n" , $1,$2}' test1
    用户名称     用户ID
    abc			123

7.awk模式
	cat test2
    anc afds vasdf casdf dfdf
    asf fd
    fdsa fdf 56
    
    awk 'NF==5 {print $0}' test2
    anc afds vasdf casdf dfdf
    
    awk 'NF>2 {print $0}' test2
    anc afds vasdf casdf dfdf
    asf fd
    fdsa fdf 56
    
    awk 'NF<4 {print $0}' test2
    asf fd
    fdsa fdf 56
    
    awk '$1==asf {print $0}' test2
    asf fd
关系运算符 含义 用法示例
< 小于 x < y
<= 小于等于 x <= y
== 等于 x == y
!= 不等于 x != y
>= 大于等于 x >= y
> 大于 x > y
~ 与对应的正则匹配则为真 x ~ /正则/
!~ 与对应的正则不匹配则为真 x !~ /正则/
	1、空模式  # 不适用任何模式就是空模式
	2、关系运算模式
	3、正则模式
	4、行范围模式
	5、BEGIN/END模式    
    
8.正则模式
	awk '/^zsy/{print $0}' /etc/passwd  # 找出以zsy开头的行
    唯一的区别就是,在grep命令中,直接使用了正则表达式,而在awk命令中,正则表达式被放入了两个斜线中。
    
    awk -v FS=':' 'BEGIN{printf "%-10s\t %s\n" , "用户名", "用户ID"}/^zsy/{printf "%-10s\t %\n" , $1,$2}' /etc/passwd
    1、从/etc/passwd文件中找出符合条件的行(用户名以zsy开头的用户)。
	2、找出符合条件的文本行以后,以":"作为分隔符,将文本行分段。
	3、取出我们需要的字段,格式化输出。
	4、结合BEGIN模式,输出一个格式化以后的文本,提高可读性。
    
    awk '/\/bin\/bash$/{print $0}'  /etc/passwd  # 找出以/bin/bash结尾的行
    
    cat tests3
    hey
    heey
    heeey
    heeeey
    
    awk -r '/he{2,3}/{print $0}' test3   # -r使用扩展正则
    heey
    heeey
    
9.awk行模式范围
	cat -n test4
    1 allent phillips
    2 green lee
    3 william aiden james lee
    4 angle jack
    5 tyler kevin
    6 lucas thomas
    7 kevin
    
	akw '/lee/,/kevin/{print NR,$0}' test4 # 匹配从出现lee的行到kevin的行
    2 green lee
    3 william aiden james lee
    4 angle jack
    5 tyler kevin
    
    awk 'NR=>2 && NR<=5{print NR,$0}' test4
    2 green lee
    3 william aiden james lee
    4 angle jack
    5 tyler kevin
关系运算符 含义 用法示例
< 小于 x < y
<= 小于等于 x <= y
== 等于 x == y
!= 不等于 x != y
>= 大于等于 x >= y
> 大于 x > y
~ 与对应的正则匹配则为真 x ~ /正则/
!~ 与对应的正则不匹配则为真 x !~ /正则/
	cat test5
    192.168.231.16
    10.0.0.1
    10.15.13.1
    192.168.131.176
    192.168.71.154
    
    awk -r '$2~/192\.168\.[0-9]{1,3}\.[0-9]{1,3}/{print $1,$2}' test5
    192.168.231.16
    192.168.131.176
    192.168.71.154
    
    
10.awk动作
    cat test6
    he  y
    he  ey
    he eey
    he  eeey
    
    awk -r '{print $0}{print $1}' test3  # 组合语句
    he 
    y
    he 
    ey
    he 
    eey
    he 
    eeey
    
11.akw条件语句
	if语句:
	awk -r '{print $0 ;print $1}' test3  # 组合语句 ,等价于上面
    
    awk ' {if(NR==1){print $0}}'   # 条件语句
    
    awk '{if(NF==1){print $1} else{print $2}}' /etc/passwd
    awk '{if(NF==1){print $1} else if(NF==2 && NF>3){print $0} else{print $2}}' /etc/passwd  #多分支
    
    循环语句:
    awk 'BEGIN{ for(i=1;i,=6;i++){print i}}'
    1
    2
    3
    4
    5
    6
    
    awk -v i=1 "BEGIn{ while(i<=5){print i;i++}}"
    1
    2
    ..
    
    awk 'BEGIn{i=1;while(i<=5){print i;i++}}'  # 同上
    
    akw 'BEGIn{i=1;do{print "test"; i++} while(i<1)}'
    test
    
    akw 'BEGIn{do{print "test"; i++} while(i<5)}' #无论如何都执行一次都之后的语句
    test
    test
    ..
    
    关键字:
    continue的作用:跳出"当前"循环
	break的作用:跳出"整个"循环
    exit的作用:跳过awk执行语句,执行END中的内容
    next的作用:跳过这行去执行接下来的行
    awk 'BEGIN{for(i=0;i<6;i++){print i}}'
    awk 'BEGIN{for(i=0;i<6;i++){if(i==3){continue};print i}}'
    0
    1
    2
    4
    5
    awk 'BEGIN{for(i=0;i<6;i++){if(i==3){break};print i}}'
    0
    1
    2
	
    # exit直接结束去执行END中的语句
    akw 'BEGIn{i=1;do{print 1; exit;print 2}{print "fadsf"}END{print "over"}'
    1
    over
    
    # 跳过第二行
    awk '{if NR==2}{next}{print $0}' test6
    
12:awk数组:awk中的数组默认下标从1开始
    awk 'BEGIN{huluwa[0]="1";huluwa[1]="2";huluwa[2]="3" ; printhuluwa[1]}'
    如果我们直接引用这个不存在的元素,awk会自动创建这个元素,并且默认为这个元素赋值为"空字符串";所以在akw中我们不能是用if(huluwa[5]="")进行判断.一个元素是否为空,有可能它不存在,创建了一个.
    awk 'BEGIN{huluwa[0]="1";huluwa[1]="2";huluwa[2]="3" ; printhuluwa[4]}'
    空
    
    如何进行判断呢?
    awk 'BEGIN{huluwa[0]="1";huluwa[1]="2";huluwa[2]="3" ; if(6 in huluwa){print "6存在就打印出来"}}'
    
    # 使用!取反
    awk 'BEGIN{huluwa[0]="1";huluwa[1]="2";huluwa[2]="3" ; if(!(6 in huluwa)){print "6存在就打印出来"}}'
    
    # 关联数组
    awk 'BEGIN{huluwa["一号"]="1";huluwa["二号"]="2";huluwa["三号"]="3" ; if(!("三号" in huluwa)){print "6存在就打印出来"}}'
    
    # delete删除数组
     awk 'BEGIN{huluwa["一号"]="1";huluwa["二号"]="2";huluwa["三号"]="3" ; print huluwa["一号"],delete huluwa;print huluwa["二号"]}'
     1
     空
     空 
     
   awk 'BEGIN{huluwa[0]="1";huluwa[1]="2";huluwa[2]="3" ; for(i=1;i<=3;i++){print i,huluwa[i]}}'
    1
    2
    3
    
    # 下表不规律的时候
    awk 'BEGIN{huluwa[0]="1";huluwa[8]="2";huluwa[11]="3" ; for(i in huluwa){print i,huluwa[i]}}'
    2
    3
    1
    注意:在这种语法中,for循环中的变量"i"表示的是元素的下标,而并非表示元素的值,所以,如果想要输出元素的值,则需要使用"print 数组名[变量]"
    注意:当数组中的下标为"字符串"时,元素值输出的顺序与元素在数组中的顺序不同,这是因为awk中的数组本质上是关联数组,所以默认打印出的元素是无序的。
    这就是以数字作为下标的优势,因为第一种for循环语法中的变量"i"为数字,由于for循环的原因,"i"是按照顺序递增的,当"i"的值与下标的值相同时,我们即可按照下标的顺序,输出对应元素的值,换句话说就是,我们是通过下标的顺序,输出对应元素值的顺序,也就是键值定位。但是,即使数组元素的下标为数字,如果使用第二种for循环语法,也不能够按照顺序输出,示例如下。
    awk 'BEGIN{huluwa[0]="1";huluwa[8]="2";huluwa[11]="3" ; for(i in huluwa){print i,huluwa[i]}}'
    2
    3
    1
    
    数值运算
    awk 'BEGIN{ a=1 ;print a; a++ ;print a}'
    1
    2
    awk 'BEGIN{ a="test" ;print a; a++ ;print a}'  # 将字符串当做0相加,空字符串也一样
    test
    1
    
    awk 'BEGIn{print array[x]; array[x]++;print array[x]}'
    空
    1
    
    cat test7
    18.43.31.4
    183.65.31.4
    18.22.31.4
    18.43.31.4
    
    awk '{count[$1]++} END{for (i in count){print i,count[i]}}' test7
    18.43.31.4 2
    183.65.31.4 1
    18.22.31.4 1
    
    cat -n test4
    allent phillips
    green lee
    william aiden james lee
    angle jack
    tyler kevin
    lucas thomas
    kevin
    
    awk '{for (i in NF){count[$i]++}} END{ for{j in count}{print j,count[j]}}' test4
    jack 1
    aiden 1
    james 1
    ..
    lee 2
    ..
    
13.awk 内置函数
	awk 'BEGIN{srand(); print rand()}'  # 生成随机数
    0.134324
    
    awk 'BEGIN{srand(); print int(rand()*100)}'  # 生成100以内的随机数
    0.134324
    
    cat test8
    allen fdsaf
    
    awk "{ gsub("l","L",$1); print $1 }" test8  # 全局字符替换,制定列的全部
    aLLen
    
    awk "{ sub("l","L",$1); print $1 }" test8  # 单字符替换,每一行中的第一匹配到的
    aLLen
    
    awk '{for(i=1;i<=NF;i++){print $i,length($i)}}' test8 # 输出字符串长度
    allen 5
    fdsaf 5
    
    awk '{for(i=1;i<=NF;i++){print $i,length()}}' test8 # 输出字符串长度,不指定列的时候,代表整行的长度
    allen fdsaf 12
   
	cat test9
    allen phillips
    green lee
    william ken allen
    
    akw '{print index($0,"lee")}' test9
    0
    7
    0
    
    在每一行中咋找字符串"Lee",如果Lee存在于当前行,则返回字符串Lee位于当前行的位置,如果Lee不存在于当前行,则返回0,表示当前行并不存在Lee,如上图所示,第二行中包含Lee,而且Lee位于第二行的第7个字符的位置,所以返回数字7。
    
    awk -v ts="一号:二号:三号" 'BEGIN{split(ts,huluwa,":")for (i in huluwa){print huluwa[i]}}'  # 使用split生成数组
    一号
    二号
    三号
    
    被split函数分割后的数组的元素下标从1开始,不像其他语言中的数组下标是从0开始的,而且数组中元素输出的顺序可能与字符串中字符的顺序不同,
    
    awk 'BEGIN{t["a"]=66;t["b"]=77;t["c"]=8;for (i in t){print i, t[i]}}'
    a 66
    b 77
    c 8
    
    awk 'BEGIN{t["a"]=66;t["b"]=77;t["c"]=8;asort(t); for (i in t){print i, t[i]}}'
    1 3
    2 66
    3 77
    数组中元素的值均为数字,但是下标为自定义的字符串,通过asort函数对数组排序后,再次输出数组中的元素时,已经按照元素的值的大小进行了排序,但是,数组的下标也被重置为了纯数字,其实,asort还有一种用法,就是在对原数组元素值排序的同时,创建一个新的数组,将排序后的元素放置在新数组中,这样能够保持原数组不做任何改变,我们只要打印新数组中的元素值,即可输出排序后的元素值
    
    awk 'BEGIN{t["a"]=66;t["b"]=77;t["c"]=8;asort(t, newt); for (i in t){print i, t[i]}}'
    a 66
    b 77
    c 8 
    
    awk 'BEGIN{t["a"]=66;t["b"]=77;t["c"]=8;asort(t, newt); for (i in newt){print i, newt[i]}}'  # 生成新数组
    1 3
    2 66
    3 77
    
    awk 'BEGIN{t["a"]=66;t["b"]=77;t["c"]=8; len=asort(t, newt); for (i=1;i<=len;i++){print i, newt[i]}}'  # 生成新数组,asort的返回值是数组的长度
    1 3
    2 66
    3 77
    
    使用asort 函数可以根据元素的值进行排序,而使用asorti 函数可以根据元素的下标进行排序。
     awk 'BEGIN{t["f"]=66;t["b"]=77;t["e"]=8; len=asorti(t, newt); for (i=1;i<=len;i++){print i,newt[i]}}'
    1 b
    2 e
    3 f 
    当数组的下标为字符串时,asorti 函数会根据原数组中的下标的字母顺序进行排序,并且将排序后的下标放置到一个新的数组中,并且asorti函数会返回新的数组的长度
   awk 'BEGIN{t["f"]=66;t["b"]=77;t["e"]=8; len=asorti(t, newt); for (i in newt){print i,newt[i],t[newt[i]]}}'
    1 b 77
    2 e 8
    3 f 66 
    
14:awk三元运算符与打印奇偶行
    我们可以判断用户的UID是否小于500,如果用户的UID大于500,则用户为普通用户,如果用户的UID小于500,则用户为系统用户。
    awk -F: 'if {($3 < 500){print "系统用户"} else if ($3 > 500){print "普通用户"}}' /etc/passwd
    
    awk -F: 'BEGIN{usertype=$3>500?"普通用户":"系统用户"; print $1,usertype}' /etc/passwd   
    条件 ? 结果1 : 结果2
    
    awk -F : '{$3>500?a++;b++}END{ print a,b}' /etc/passwd
    42 7   # 统计普通用户和系统用户数量
    
    akw '1{print $0}' test3  == awk '1' test3   # 其中的1可以理解为一种模式,1表示为真
    1 hey
    2 heey
    3 heeey
    
    akw '0{print $0}' test3  == awk '0' test3   # 其中的0可以理解为一种模式,0表示为假,所以不输出匹配的东西
    
    akw '!0{print $0}' test3  == awk '!0' test3   # 其中的0可以理解为一种模式,0表示为假,所以不输出匹配的东西,但是!表示取反,所以这个时候!0为真
	1 hey
    2 heey
    3 heeey
    
    awk 'i=!i' test3    # 指数出奇数行,第一i为空(假),取反之后为真,第二次i真,取反之后为假,以此类推,第三次i为第二次的假,取反之后为真
    2 heey

1.cpu:

内置变量:
    $0 表示显示整行 
    $NF表示当前行分割后的最后一列($0和$NF均为内置变量),NF表示有多少列,$NF表示最后一列,进行运算$(NF-1)倒数第二列

参考:
http://www.zsythink.net/?s=awk
https://blog.51cto.com/12758568/2122677

猜你喜欢

转载自www.cnblogs.com/liuzhanghao/p/12970697.html