linux awk 3

来源:互联网 发布:大数据课程学生服务站 编辑:程序博客网 时间:2024/06/06 17:40

awk的基本功能是对文件进行指定规则浏览和抽取信息。
基本格式:
(1) awk [-F 分隔域] 'command' input-file(s)
(2) 写入shell脚本中
(3) awk -f awk-script-file input-file(s)

注意:这里如果使用if等编程语句,要用{}括起来。


test

name    grade    score    id
hover    2        96        2003073
twq        3        91        2003074
zsm        4        92        2003075
hzm        5        95        2003076
bl        6        96        2003077


1,文本过滤处理:
(1) awk '{print $0}' test   #打印文件的全部内容
注意:这里awk使用函数print用来打印整个文件的内容。其中的$0就表示整个文件的内容。

(2) awk '{print $1}' test   #抽取文件test中的第一列
注意:如果awk没有使用-F指定分隔符号,默认的分隔符号是空格和TAB键。

#列出所有的用户名和登陆的shell名
awk -F : '{print $1,$6}' /etc/passwd

(3) awk -F : '$1=="root" {print $0}' /etc/passwd
指打印用户名为root的那一行

2,文本格式定制
(1)给输出信息加上表头
awk -F : 'BEGIN {print "name        shell\n--------------------------------"}
    {print $!"\t"$6}' /etc/passwd

(2)
awk -F : 'BEGIN {print "name        shell\n--------------------------------"}
    {print $!"\t"$6} END {"end-of-report"}' /etc/passwd

3,在awk中使用正则表达式
^                表示匹配行首的字符
[...]            匹配[]正的任意一个字符
(str1|str2)        匹配含有str1或str2的行       
.                匹配任意一个字符


(1)匹配
为使一域匹配一正则表达式,可以使用以下两种方法:
    1)$n~正则表达式
    2)if($n~正则表示式) print $0

awk -F: '$0 ~ /^root/' /etc/passwd  #打印以root开头的行
awk -F: '{if($0 ~ /^root/) print $0}' /etc/passwd  #和上一句等效

*精确匹配*
#打印名字为root的用户在/etc/passwd文件中的记录
awk -F : '$1=="root" {print $0}' /etc/passwd
#打印路径为/root的用户在/etc/passwd中的记录
awk -F : '$6=="\/root" {print $0}' /etc/passwd

4,在awk中使用条件操作符
<    小于        >=    大于等于
<=    小于等于    ~    匹配正则表达式
==    等于        !~    不匹配正则表达式
!=    不等于

(1)模糊匹配
i)使用if         {if($1~/zhengxh/) print $0}
ii)不用if        '$0 ~ /zhengxh/'
ex:
    awk '$0~/zhengxh/' filename
    或awk '{if($0~/zhengxh/) print $0} filename    #输出含有zhengxh的行
    或awk '/zhengxh/' filename

(2)精确匹配
$n=="chars"
    awk '$1=="zhengxh" {print $0}' filename        #输出第一列等于zhengxh的行

(3)反向匹配
$n !~ /adf/
    awk '$1 !~ /zhengxh/ {print $0}' filename    #输出第一列不是zhengxh的行

(4)大小写匹配
    awk '/[zZ]hengxh/'  filename     #匹配含有zhengxh 或是Zhengxh的字符串
   
(5)使用或运算
    awk '$0 ~ /(zhengxh|hover)/' filename     #查找含有zhengxh或hover字串的行
    或awk '{if($0~/zhengxh/ OR $0~/hover/) print $0}' filename

(6)内置变量
ARGC    命令行参数个数
ARGV    命令行参数排列
ENVIRON    环境变量支持队列的
FNR        浏览文件的记录数
FS        设置输入域分隔符,与-F同
NF        记录域的个数
NR        已读的记录数
OFS        输出域分隔符
ORS        输出记录分隔符
RS        控制记录分隔符


awk 的具体运用(FAQ)
* 把一个文件中满足条件的放到一个文件不满足条件的放到另一个文件
awk -F: '{if(NF==6) print $0 > "yes" else print $0 > "no" }' filename


*如何在awk中使用变量
要注意的是在awk中的表达式一般是用''号括起来的,在shell中单引号是全屏蔽符,所以用单引号使得变量无法生效,在使用shell变量时,可以这样使用

##########################

#/bin/sh
#

name="zhengxh"

count=`awk -F: '
    /'${name}'/{        #这里需要使用变量的地方把变量隔开
        sum+=$3   
    }
   
    END { print sum }
'`

*使用awk输出文件的倒数第N行
tail -n $N $filename | awk '{if(NR==1) print $0}'

*)如何在AWK中使用外部变量
1)aa="aaaaaaa"
awk '{print "'"${aa}"'"}' $filename
2)
以下使用外部变量时有错:
#!/bin/bash
filepath=/etc/passwd
user=root
result=`awk -F":" '/$user/ { print $1}' $filepath`
echo $result
$
改正:
awk -F':' '/'"$user"'/{print $1}' $filepath





*如何把AWK中的值,传送到外部的SHELL变量
使用$() 或 ``
aa=`awk -F: '{print $0}' $filename`

*进行统计
文件aa.txt
一个用户可能有多个记录,这时只统计一次:

数据          用户ID        下载文件名称   用户所在地  等。。。。。
20071128,0001,1,null,600571021800,028 
20071128,0002,1,null,600571001800,021
20071128,0002,1,null,600571001802,021
20071128,0003,1,null,600571031800,020
20071128,0004,1,null,600571001800,010

统计各个的号码(最后一个字段)数量
awk -F, '{if(!b[$2$6]){a[$6]++;b[$2$6]++}}END{for(i in a){print i,a[i]}}'

*如何把多个语句放在一句话(一行)中处理
cat "$file" | awk '{ip=$1; i=index($0,"\google"); if(i>1){ua = substr($0,i); print ip "\t" ua}}'

*把记录
aa,bb,cc [name, address, age]
变成记录
ip,aa,bb,cc [ip,name,address,age]

awk -F,    '{pirntf "\"192.168.5.154\","$0}'

*取一个字符串的首字母
str=abc
echo ${str:0:1}
echo $str|awk '{print substr($0,1,1)}'
echo $str|sed 's/\(.\).*/\1/'

*如何在一字符串的前面加上字符串 addtext
awk '{print "addtext \""$0"\""}' temp

*计算不重复的列的总和
aa|001|23
ac|001|23
bb|002|213
cc|004|32
dd|005|34

awk -F'|' '!a[$1]++{sum+=$3} END{print sum} ' filename

*定义多个分界符
aa cc dd
bb,ee ff

awk -F'[ ,]' '{print $3}' filename
 
有时候有可能出现多个分隔符号,但是我们需要把它当成一个,这时就要用:
***
#echo "adf::adf:f" | awk -F'[:]+' '{print $2}'
#adf
***
#echo "adf::adf:f" | awk -F':' '{print $2}'
#                            #输出空
***

*如何用awk处理这样的文件格式?
源文件格式:
  
表1313
                               客户经理业绩情况表(月报表)
支局所名称:XXXX储蓄所    月份:10     
客户经理
业绩(累计日积数)
酬金
姓名                  代号     活期               整整3月
整整6月               整整1年            整整2年                整整3年
整整5年             整整8年               零整1年             零整3年
零整5年              定活两便        
兰                  37040576  0.00                 0.00                 0.00
0.00                 0.00                 0.00                 0.00
0.00                 0.00                 0.00                 21340.00
0.00                 0.00                 
禚树征              37040585  27277.21             120.00               0.00
2965.22              0.00                 0.00                 0.00
0.00                 0.00                 0.00                 0.00
0.00                 845615151469035520.00
秦                  37040502  20094.30             0.00                 0.00
0.00                 0.00                 0.00                 0.00
0.00                 0.00                 0.00                 0.00
0.00                 622937933443235840.00
           合     计          444868.41            0.00                 0.00
2965.22              0.00                 0.00                 10764.20
0.00                 0.00                 0.00                 0.00
18600.00             13791245122257916000.00

如何用awk处理成以下格式(只要里面的记录并用逗号分割每个字段,最后一个字段不要,表头和表尾不要):
"兰",37040576,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,21340.00,0.00                 
"禚树征",37040585,27277.21,120.00,0.00,2965.22,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00
"秦",37040502,20094.30,0.00,0.00,0.00,0.00,0.00,,0.00,0.00,0.00,0.00,0.00,0.00

awk '{sub("[ \t]", ","); print $0}' temp
awk '{sub("[ ,\t]", ","); print $0}' temp

*如何用TAB做分隔符号
awk -F'\t'
awk -F'[ \t]'  #默认是这样,以空格或TAB做分隔符
一般模式:awk -F'rexp' '{}' filename
这里rexp是一个正则表达式,用来指示要使用的分隔符。


*--------------------------
在awk脚本中有下面一段

BEGIN{split("123#456#789",team,"#")}
END{for(i in team) print team}

预测的输出应该是
123
456
789
啊!

为什么实际的输出是
456
789
123
呢?

答:
---
while(++i in team) print team[i]

----------------------------

*-awk中使用system()
answer:
ls | awk '{if (system("ls " $0) == 0) {print "file " $0 " exists !"}}'


*-调用外部命令和awk结合
使用getline得到外部命令的输入f
 ls | awk '{getline ll; print $ll}'

--------------------------
*-只输出第一行的内容
awk '{print; exit}'

*-多shell命令
awk ’BEGIN{while("dir|sed 1,3!d"|getline)print $1}‘

*-在awk的输出中加单引号
#cat file
rwxrwsrwx   gprs    512 GPRS

awk '{printf("%s %s '"'%s'"' %s\n",$1,$2,$3,$4)}' file
    在这里要理解的是:上面的表达式分成了三块,前面的''内的内容是一块,中间的双引号内的内容是一块,最后的单引号里的内容是一块;由于单引号在双引号中的作用被屏蔽,所以输出的变量会带上单引号。从而达到预定结果。

*-得到df -h 显示出来的百分数字(去掉百分号)
(1)df -h | awk '{if(NR!=1) print $5}' | cut -d% -f 1
(2)df -h | awk -F'[ \t%]+' '{if(NR!=1) print $5}'


***
实现加和
中间结果如下所示:
CPU usr sys idl
0 13 4 76
1 9 4 79
要想显示下列结果:
1 22 8 155
***
cat data | awk '{if(NR!=1) a+=$1;b+=$2;c+=$3} END{print a,b,c,d,e}'

***
如何实现以几个字母中任意一个打头的字符串的查找
cat data
Mike Harrington:(510) 548-1278:250:100:175
Christian Dobbins:(408) 538-2358:155:90:201
Susan Dalsass:(206) 654-6279:250:60:50
Archie McNichol:(206) 548-1348:250:100:175
Jody Savage:(206) 548-1278:15:188:150
Guy Quigley:(916) 343-6410:250:100:175
Dan Savage:(406) 298-7744:450:300:275
Nancy McNeil:(206) 548-1278:250:80:75
John Goldenrod:(916) 348-4278:250:100:175
Chet Main:(510) 548-5258:50:95:135
Tom Savage:(408) 926-3456:250:168:200
Elizabeth Stachelin:(916) 440-1763:175:75:300

awk -F'[ :]' '$1~/^[MJ] {print $1}'

输出以D开头的域
awk -F':' '{for(i=1;i<=NF;i++) if($i~/^D/) print $i}' data2



****每一行的和
统计
#cat ab
a 1
a 2
b 2

awk '{a[$1]+=$2} END{for(i in a) print i,a[i]}' file


-----------------------------------
*对两个文件的处理
----------
大家好,想请教一个问题,我现在有两个文件,如下所示,这两个文件格式都是一样的。我想首先把文件2的第五列删除,然后用文件2的第一列减去文件一的第一列,把所得的结果对应的贴到原来第五列的位置,请问这个脚本该怎么编写?
file1:
50.481  64.634  40.573  1.00  0.00
51.877  65.004  40.226  1.00  0.00
52.258  64.681  39.113  1.00  0.00
52.418  65.846  40.925  1.00  0.00
49.515  65.641  40.554  1.00  0.00
49.802  66.666  40.358  1.00  0.00
48.176  65.344  40.766  1.00  0.00
47.428  66.127  40.732  1.00  0.00
51.087  62.165  40.940  1.00  0.00
52.289  62.334  40.897  1.00  0.00
file2:
48.420  62.001  41.252  1.00  0.00
45.555  61.598  41.361  1.00  0.00
45.815  61.402  40.325  1.00  0.00
44.873  60.641  42.111  1.00  0.00
44.617  59.688  41.648  1.00  0.00
44.500  60.911  43.433  1.00  0.00
43.691  59.887  44.228  1.00  0.00
43.980  58.629  43.859  1.00  0.00
42.372  60.069  44.032  1.00  0.00
43.914  59.977  45.551  1.00  0.00

--答--
awk 'NR==FNR{a[NR]=$1}NR!=FNR{$5=a[FNR]-$1;print}' file2 file1

说明:
当NR==FNR时,是第一个文件,到了第二个文件时FNR会从0开始计数,而NR却继续在原来的基础上自增。
先把第一个文件中要使用的内容保存到一个数组中,然后在处理第二个文件时才使用。
这里包含了很好的处理多个文件的方法,值得借鉴。
-------------------------------------------------


**
源文件如下:
37123456,123456789,601234020200051640,"孔霞","03",123456789,"2008/01/06",1,4000,5060.41
37123456,123456789,601234020200062521,"栗汝礼","03",123456010,"2008/01/06",1,100,110.91
37123456,123456789,601234020200069800,"柯纯龙","03",370786017,"2008/01/06",1,20000,19500
37123456,123456789,601234020200069800,"柯纯龙","03",123456030,"2008/01/06",1,31000,500
37123456,123456789,601234020200068018,"严凤书","03",123456789,"2008/01/06",2,50000,100163.39
37123456,123456789,601234020200070039,"刘庆","03",123456789,"2008/01/06",2,4000,8000
37123456,123456789,601234020200060554,"王兰英","03",123456789,"2008/01/06",1,1600,91.26
37123456,123456789,601234020200070039,"刘庆","03",123456789,"2008/01/06",2,4000,4000
37123456,123456789,601234020200067710,"罗有艳","03",123456789,"2008/01/06",2,3000,12012.01
37123456,123456789,601234020200064742,"孙祥婷","03",123456789,"2008/01/06",1,50,12.61
37123456,123456789,601234020200069800,"柯纯龙","03",123456030,"2008/01/06",1,200,300
37123456,123456789,601234020200060554,"王兰英","03",123456789,"2008/01/06",1,1000,1091.26
处理要求如下:
如果第三列中的数据是唯一的,就保留这一行,如果有重复的,就保留最后一个记录行。

--------答---------
awk -F, '{a[$3]=$0}END{for( i in a)print a[i]}' urfile

------------------------------

*如何快速获取特定字符串的前2(n)行和后2(n)行
$cat file

put 8
put 9
put 10
abc
put 11
put 12
put 13
put 14
abc
put 15
put 16
put 17
put 18
abc
put 19
put 20
put 21
put 22
put 23
abc
put 24
put 25
put 26
put 27
abc
put 28
put 29
put 30
put 31
put 32
abc
put 33
put 34
put 35
put 36
put 37

$ awk '{a[NR]=$0}/abc/{for(i=2;i>=0;i--) print a[NR-i];for(j=1;j<=2;j++){if(!getline) exit;print}print ""}' urfile
小结:
(1)这里使用了数组的功能,数组在作为缓存是普遍用法,要记住。
但这里如果文件超大的话,缓冲区的负担太大,应换存自己需要的哪些行(这里我只换存了4行)
$ awk '{a[NR%4]=$0}/abc/{for(i=2;i>=0;i--) print a[(NR-i)%4];for(j=1;j<=2;j++){if(!getline) exit;print}print ""}' urfile
(2)使用了getline函数-- $ man awk  就知道,getline函数的作用是: 1)读取下一行2)set $0
注意这两个元素的应用.



*)如何提取aaaa12123adsf adfadfbbbb 的aaaa和bbbb中间的内容
$cat file
adfaaaaadfadfadfdfadabbbb
adfaaaaadf  ad323452 adfadfdfadabbbbz
$
1)sed -e 's/.*\(aaaa\)\(.*\)\(bbbb\).*/\2/g' file
2)sed -e 's/.*\(a\{3\}\)\(.*\)\(b\{3\}\).*/\2/g' file
3)echo "aaaa121312dfadfbbbbb" | awk -F'aaaa|bbbb' '{print $2}'

*)我以如下字符要处理

a  123
b  124
c   125
a   126
d   127
e   128
ac  129

如果第一列出现两次或两次以上将其打印出来,并计算出现次数。
$ awk '{a[$1]++;} END{for(i in a) if(a[i]>=2) {print i,a[i]}}' file
分析: 由以上的命令可以得到流程:
a["a"] 2
a["b"] 1
a["c"] 1
a["d"] 1
a["e"] 1
a["ac"] 1
而for(i in a)  则是要遍历的i的值是:a,b,c,d,e,ac



*)去掉重复的行
awk '!a[$0]++' file

*)去掉重复行,并保持顺序不变
awk 'f[$0]!=1{print;f[$0]=1}'

*)打印重复行
1)awk '{a[$0]++} END{for(i in a) {if(a[i]>1){print i}}}' filename
2)sort cc  | uniq -dc

原创粉丝点击