AWK用法小结2

awk 的內建函数(Built-in  Functions)        

    一、字符串函数 
    语法:index( 原字符串,寻找的子字符串):
    解释:若原字符串中含有欲找寻的子字符串,则返回该子字符串在原字符串中第一次出现的位置,如果没有出现该子字符串则返回0。
    例如执行:
    [root@myfreelinux pub]# awk ‘BEGIN{print index(“0411-8888-9999″,”-8″)}’
    5   是返回值,实际上检索到“-8”时,“-”在第五位,所以返回值就是5了。

                   
    语法:length(字串)
    解释:返回该字串的长度。 
    例如执行:
[root@myfreelinux pub]# awk ‘BEGIN{print length(“0411-8888-9999″)}’
14 是返回值

    语法:match( 原字串,寻找对比的正则表达式)
    解释:awk会在原字串中寻找复合正则表达式的子字符串,如果复合正则表达式的字符串 有多个,以原字符串中最左侧的子字符串为准。 
    awk找到该字符串后会根据字符串设定以下awk内部变量的值,比如RSTART和 RLENGTH
    RSTART=符合条件的子字符串在原字符串中的位置,如果=0表示没有找到合条件的子字符串。 
    RLENGTH = 符合条件的子字符串长度,如果=-1表示没有找到符合条件的子字符串。
    比如看一下两个例子:
[root@myfreelinux pub]# awk ‘BEGIN{match(“banana”,”an”);print RSTART,RLENGTH}’
2 2
[root@myfreelinux pub]# awk ‘BEGIN{match(“banana”,/(an)+/);print RSTART,RLENGTH}’
2 4
可 以看到这两条语句执行的结果不一样,因为第一条的正则表达式只是寻找“an”,而第二条是寻找”an”的多个多个重复组合,所以有两组”an”,长度是 4。

    语法:split( 原字符串,数组名称,分隔字符)
    解释:
 awk将根据指定的分隔 字符(field separator)来分隔原字符串,将原字符串分割成一个个的域(field),并以指定的数组保存各个域的值。
    例如:
[root@myfreelinux pub]# awk ‘BEGIN{str=”root:x:0:0:root:/root:/bin/bash”;split(str,array,”:”);for(one in array) print one,array[one];}’
4 0
5 root
6 /root
7 /bin/bash
1 root
2 x
3 0

    语法:sprintf(格式字符串,项1,项2,….)
    解释:该函数的用法与awk或C语言的输出函数printf()相似,不同的是sprintf()会要求打印出的结果当成一个字符串返回。一般常用 sprintf()来改变资料格式。例如:x为一数值,若欲将其变成一个含二位小数的数值,可执行如下指令:
[root@myfreelinux pub]# awk ‘BEGIN{x=2;x=sprintf(“%.2f”,x);print x}’,执行结果是2.00

    语法:sub( 比对用的正则表达式,新字符串,原字符串) 
   解释:sub( )将原字符串中第一个(最左边)符合正则表达式的子字符串替换为新字符串。第二个参数“新字符串”中可用”&”来表示“符合条件的字符串”。承上 例,执行下列指令:
[root@myfreelinux pub]# awk ‘BEGIN{A=”a6b12anan212.456an12″;sub(/(an)+[0-9]*/,”[&]“,A);print A}’
a6b12[anan212].456an12
[root@myfreelinux pub]# awk ‘BEGIN{A=”a6b12anan212.456an12″;sub(/(an)+[0-9]*/,”|&|”,A);print A}’
a6b12|anan212|.456an12
[root@myfreelinux pub]# awk ‘BEGIN{A=”a6b12anan212.456an12″;sub(/(an)+[0-9]*/,”",A);print A}’
a6b12.456an12
[root@myfreelinux pub]# awk ‘BEGIN{A=”a6b12anan212.456an12″;sub(/(an)+[0-9]*/,”999″,A);print A}’
a6b12999.456an12
    由以上四个例子可以看出,&表示匹配的字符串,对于新字符串,需要用双引号引起来,比如“999”,那么匹配的字符串就会替换成999,而如果双 引号内没有任何字符的时候,就是将匹配的字符串给删除。
    sub() 与match()搭配使用,可依次取出原字符串中符合指定条件的所有子字符串。 例如执行下列程式:
[root@myfreelinux pub]# vi submatch.awk
#!/bin/awk
BEGIN{
data=”p12-p24 p56-p66″;
while(match(data,/[0-9]+/)>0){
        print substr(data,RSTART,RLENGTH);#RSTART表示匹配的位置,RLENGTH表示匹配的长度,这一行是打印匹配的数字
sub(/[0-9]+ /,”",data);#将匹配的数字用空字符串代替
print data;} #匹配的数字打印后,用空字符串代替,原data字符串内容发生变化
}
执行并查看运行结果如下:
[root@myfreelinux pub]# awk -f submatch.awk
12
p-p24 p56-p66
24
p-p p56-p66
56
p-p p-p66
66
p-p p-p awk ‘ BEGIN { data = “p12-P34 P56-p61″
    sub( )中第三个参数(原字符串)如果没有指定,默认是$0。比如可用sub( /[9-0]+/,”digital” ) 表示sub(/[0-9]+/,”digital”, $0 )

    语法:gsub(正则表达式,新字符串,原字符串)
    解释: 这个函数与sub()一 样,是进行字符串取代的函数。不同点是gsub()会取代所有合条件的子字符串,而sub函数只会取代同一行中第一个符合条件的字符串,gsub()会返 回被取代的子字符串个数。
    同样是一个程序,只将sub函数替换为gsub函数,再来看一下执行效果:
[root@myfreelinux pub]# vi submatch.awk
#!/bin/awk
BEGIN{
data=”p12-p24 p56-p66″;
while(match(data,/[0-9]+/)>0){
        print substr(data,RSTART,RLENGTH);
gsub(/[0-9]+/,”",data);#其他的都不变,只将sub函数替换 为gsub函数,输出结果变化很大
print data;}
}
[root@myfreelinux pub]# awk -f submatch.awk 
12
p-p p-p  #输出结果只有两行,这就是sub和gsub的不同点
    通过上面这个例子,对比一下sub中的例子,就能明显的区分出sub和gsub函数的区别。

    语法:substr( 字符串,起始位置 [,长度] )
    解释:
 返回从起始位置起, 指定长度的子字符串,如果没有指定长度,则返回起始位置到字符串末尾的子字符串。 看下例:
[root@myfreelinux pub]# awk ‘BEGIN{A=”I love todays life”;print substr(A,3);}’
love todays life
[root@myfreelinux pub]# awk ‘BEGIN{A=”I love todays life”;print substr(A,3,4);}’
love
注意空格也算字符。

    (二)、 数学函数
    语法:int(x)
    解释:返回x的整数部分,去掉小数。看下例:
   [root@myfreelinux pub]# awk ‘BEGIN{a=5.5;b=-5.5;print int(a), int(b);}’ #注意int函数是向零取整,而不是四舍五入
5 -5

    语法:sqrt(x)
    解释:返回x的平方根。 看下例:
[root@myfreelinux pub]# awk ‘BEGIN{a=4;b=-9;print sqrt(a),sqrt(b);}’
awk: warning: sqrt: called with negative argument -9
2 nan
通过上例看以看到,如果是一个负数,比如 -9,系统会提示非法参数(negative argument),并输出nan。

    语法:exp(x) 
    解释:将返回e 的x次方。 看下例:
[root@myfreelinux pub]# awk ‘BEGIN{print exp(1),exp(2);}’
2.71828 7.38906

    语法:log(x)
    解释:返回x以e为底的对数值。看下例:
[root@myfreelinux pub]# awk ‘BEGIN{print log(2.71828),log(-2);}’
awk: warning: log: received negative argument -2
0.999999 nan
     可以看出,和执行sqrt(x)一 样,x同样不能是负数,否则提示参数错误,并返回nan值。

    语法:sin(x)
    解释:x 须以弧度为单位,sin(x)将返回x的sin函数值。
    语法:cos(x)
    解释:x 须以弧度为单位,cos(x)将返回x的cos函数值 
    语法:atan2(y,x)
    解释:返回y/x 的tan反函数之值,返回值系以弧度为单位。

    语法:rand()
    解释:返回介于0与1之间的(近似)随机数值,0 <rand()<1。除非自己指定rand()函数起始的种子,否则每次执行awk程序时,rand()函数都将使用同一个內定的种子,来产 生随机数。 

    语法:srand([x])
    解释:指定以x为rand( )函数起始的种子。如果省略了x,则awk会以执行时的日期与时间为rand()函数起始的种子。

awk的内部变量的个数不多,在这里介绍的时候就不按照字母顺序排列了,而是按相关性分类说明。

    ARGC         
    ARGC表示命令行上除了选项-F,-v,-f等选项及其所对应的参数之外的所有参数的个数。如果将“awk程序”直接写在命令行上,那么ARGC是不会 把“awk程序”计算在内的。

    ARGV         
    ARGV是一个数据,用来记录命令行上的参数的名称。 执行下列命令:
[root@myfreelinux pub]# awk ‘BEGIN{printf(“ARGC=%d/n”,ARGC);for(a in ARGV)printf(“ARGV[%d]=%s/n”,a,ARGV[a]);}’ inte integer
ARGC=3
ARGV[0]=awk
ARGV[1]=inte
ARGV[2]=integer
    需要注意当ARGC = 3 时,命令列上只指定了2 个文件。
awk的参数-F/t 表示以tab 为栏位分隔字符FS(field seporator);-v a=8 是用以初始化程序中的变量。

    FILENAME
    FILENAME用来表示目前正在处理的文件名。

    FS域分隔字符
    $0 表示目前awk所读入的数据行的内容,$1,$2…示所读入的数据行经过FS指定分割符分割后的第一域,第二域…的记过。
    说明:当awk读入一行数据行”A123  8:15″ 时,会先以$0 记录,即$0 = “A123  8:15″,如果程序中进一步使用了$1,$2…或 NF等内部变量时,awk才会自动分割 $0,以便取得各域的数据。 切割后各个域的数据会分別用$1,$2, $3…等存储。
    awk默认的(default)域分隔字符(FS)为空白字符(空格及tab)。以上例来说,如果没有改变FS的值,那么分割后:第一个域 ($1)=”A123″,第二个域($2)=”8:15″。也可以使用正则表达式来定义FS,比如FS=/[ /t:]+/,表示0或多个空格、tab、:分别或他们三个任意组合成的字符串作为分割符,awk每次需要分割数据行时,就会参考目前FS的值。 那么这定FS后,$0 = “A123  8:15″,将被分割为,第一个域($1) = “A123″,第二个域($2) = “8″,第三个域($3) = “15″。

     NR (number record)
    
NR表示awk 开始执行该程序后所读取的数据行数。

   FNR (file number record)
    
FNR 与NR功用类似;不同的是awk在处理多个数据文件的时候,每打开一个新的文件,FNR便从0重新累计,而NR是一直累加,看个列子更直观些,见下列:

[root@myfreelinux pub]# cat inte
123
324
[root@myfreelinux pub]# cat integer
222 111
333 111

444 111
[root@myfreelinux pub]# awk ‘BEGIN{print NR,FNR,$0}’ inte integer
0 0
[root@myfreelinux pub]# awk ‘{print NR,FNR,$0}’ inte integer
1 1 123
2 2 324
3 1 222 111
4 2 333 111
5 3
6 4 444 111

    NF (number field)
    NF表示当前行被域分隔符分割成的域的个数。awk 每读入一笔数据后,在程序中用NF记录该行数据包含的域的个数。在下一行数据被读入之前,NF不会改变。但如果使用$0来记录数据,例如:使用 getline,此时NF将代表新的$0上数据的域的个数。

    OFS (output file separate)
    
OFS输出分 隔字符。默认是” “(一个空白)

    ORS (output Record separate)
    ORS输出数据分 隔字符。默认值是”/n”(换行符)。

    OFMT(output format)
    
OFMT数值数据的输出格式。默认 值”%.6g”(若须要时最多印出6位小数)。

    当使用print指令一次打印出多项数据时,例如:print $1,$2,输出时,awk会自动在$1与$2之间补上一个域分隔符的值(OFS 之值);每次使用print输出后,awk会自动补上h行分隔符的值(ORS 之值)。使用print 输出数值数据时,awk将采用 OFMT 之值为输出格式。例如:
[root@myfreelinux pub]# awk ‘BEGIN{print 2/3;OFS=”:”;OFMT=”%.2g”;print 2/3,1;}’
0.666667
0.67:1
    程序中通过改变OFS和OFMT的值,改变了指令print的输出格式。

    RS
    RS( Record Separator) :awk从文件上读取数据时,将根据RS的定义把数据切割成许多Records,awk一次只读入一个Record进行处理。RS 的默认值是换行符”/n”,所以一般awk一次仅读入一行数据。有时一个Record含括了几行数据(Multi-line Record),这情況下不能再以”/n” 来分隔相邻的Records,可改用空白行来分隔,即令RS = “”,表示以空白行来分隔相邻的Records。 

    RSTART
    RSTART与使用字串函数match( )有关的变量,是匹配的字符的开始的位置。
    RLENGTH
    
RLENGTH与使用字串 函数match( )有关的变量,RLENGTH是匹配的字符串的长度。当使用match() 函数后,awk会将match() 执行的结果以RSTART和RLENGTH记录。看下面的例子:
[root@myfreelinux pub]# awk ‘BEGIN{match(“banana”,”an”);print RSTART,RLENGTH}’
2 2
[root@myfreelinux pub]# awk ‘BEGIN{match(“banana”,/(an)+/);print RSTART,RLENGTH}’
2 4
[root@myfreelinux pub]# awk ‘BEGIN{match(“banana”,/(na)+/);print RSTART,RLENGTH;}’
3 4

    SUBSEP
    
SUBSEP(Subscript Separator) 数组下标的分隔字符,默认值为”/034″实际上,awk中的数组只接受字串当它的下标,比如:  Arr["John"]。但awk中仍然可使用数字当数组的下标,甚至可使用多维的数组(Multi-dimenisional Array),比 如:Arr[2,20]。事实上,awk在接受Arr[2,20]之前,就已先把其下标转换成字串”2/03420″,之后便以Arr["2 /03420"] 代替Arr[2,20]。可参考下例:
[root@myfreelinux pub]# awk ‘BEGIN{arr[2,20]=13;print arr[2,20];print arr["2/03420"];idx=2 SUBSEP 20;print arr[idx];}’
13
13
13
 再看下面这个例子,统计每门课有几个学生选修,用课程名称作为 数组的下标:
[root@myfreelinux pub]# cat kecheng.dat
zhangsan math english chinese
lisi     computer chinese english
wangwu dianzi chinese math
zhaoliu huanjing english chinese
[root@myfreelinux pub]# cat kecheng.awk
#!/bin/awk -f
{
for(i=2;i<=NF;i++)
 array[$i]++;
}
END{
for(one_array in array)
 print one_array,array[one_array];
}
[root@myfreelinux pub]# awk -f kecheng.awk kecheng.dat
computer 1
english 3
dianzi 1
chinese 4
math 2
huanjing 1

为什么要使用正则表达式
    linux/UNIX中提供了许多命令和工具,它们可以在文件中查找(Search)字符串或替换(Replace)字符串的功能。像 grep,vi,sed,awk等,不论是查找字符串还是替换字符串,都得先告诉这些命令所要查找(被替换)的字符串是什么,如果未能事先明确知道所要查 找(被替换)的字符串是什么,只知道这个字符串存在的范围或特征时,例如:(一)查找”T0.c”,”T1.c”,”T2.c”…”T9.c” 当中的任一字符串。(二)查找至少存在一个”A”的任意字符串。这种情況下,如何告诉执行查找字符串的命令所要查找的字符串是什么。例(一) 中,要查找任一在”T”与”.c” 之间存在一个阿拉伯数字的字符串;当然可以用列举的方式,一一把所要查找的字符串告诉执行命令的命令。但例(二) 中符合该条件的字符串有无限种可能,势必无法一一列举。此时,便需要另一种字符串表示的方法。

    什么是正则表达式:正则表达式(以下简称Regexp)是一种字符串表达的方式。可以指定具有某特征的所有字符串。
    注:为了与一般字符串区别,在这里,在正则表达式的字符串之前皆加 “Regexp”。
    awk程序中常以/…/括住Regexp,以区別于一般字符串。

    组成正则表达式的元素:普通字符除了 . * [ ] + ? ( ) /  ^ $ 外的所有字符。由普通字符所组成的Regexp的意义与原字符串字面意义相同。例如:Regexp “the” 与一般字符串的”the” 代表相同的意义。
    (Meta character) :用来代表任意一字符。在linux/unix Shell中使用 “*”表示0个或任意长度的字符。
    在Regexp中:
    “.” 代表任意一个字符
    “*” 另有其它涵意,并不代表任意长度的字符串
    ^ 表示该字符串必须出现于行首
    $ 表示该字符串必须出现于行末。
    例如:Regexp/^The/用来表示所有出现于行首的字符串”The”。Regexp/The$/ 用来表示所有出现于行末字符串”The”。
    /将特殊字符还原成字面意义的字符(Escape character) ,Regexp中特殊字符将被解释成特定的意义。如果要表示特殊字符的字面(literal meaning)意义时,在特殊字符之前加上”/”即可。例如:使用Regexp来表示字符串”a.out”时,不可写成 /a.out/,因为”.”是特殊字符,表示任意一个字符。可符合Regexp / a.out/的字符串将不只 “a.out” 一个;字符串”a2out”,”a3out”,”aaout”…都符合 Regexp /a.out/, 正确的用法为:/ a/.out/。

    [...]字符集合,用来表示两中括号间所有的字符当中的任一个。
    例如:Regexp/[Tt]/可用来表示字符”T” 或 “t”。所以Regexp/[Tt]he/,表示字符串”The” 或 “the”。字符集合[...] 內不可随意留空白。例如:Regexp/[ Tt ]/,其中括号內有空白字符,除表示”T”,”t” 中任一个字符,也可代表一个” “(空白字符) 。
    - 字符集合中可使用”-” 来指定字符的区间,其用法如下:Regexp /[0-9]/等于/[0123456789]/,用来表示任意一个阿拉伯数字。同理Regexp/[A-Z]/ 用来表示任意一个大写英文字母。但要注意,Regexp /[0-9a-z]/并不等于/[0-9][a-z]/;前者表示一个字符,后者表示二个字符。Regexp/[-9]/或/[9-]/只代表字 符,”9″或 “-”。
    [^...]使用[^...] 产生字符集合的补集(complement set)。其用法如下:例如:要指定”T”或”t”之外的任一个字符,可用/[^Tt]/表示。同理Regexp/[^a-zA-Z]/,表示英文字母之 外的任一个字符。
    注意”^” 的位置:”^”必须紧接在”["之后,才代表字符集合的补集。例如:Regexp /[0-9/^]/只是用来表示一个阿拉伯数字或字符”^”。

    * 表示字符重复次数的特殊字符。”*” 表示它前方之字符可出现0次或任意多次,即字符大于等于0 次。例如:Regexp/T[0-9]*/.c/中,*表示其前[0-9](一个阿拉伯数字)出现的次数可为0次或多次。所以Regexp /T[0-9]*/.c/可用来表示”T.c”,”T0.c”,”T1.c”…”T19.c” 。

    +表示其前的字符出现一次或一次以上。例如:Regexp /[0-9]+/ 用来表示一位或一位以上的数字。

    ? 表示其前的字符可出现一次或不出现。例如:Regexp /[+-]?[0-9]+/ 表示数字(一位以上)之前可出现正负号或不出现正负号。

    (…)用来括住一群字符,且把他当成一个group。例如:Regexp /12+/ 表示字符串”12″,”122″,”1222″,”12222″,…。egexp /(12)+/ 表示字符串 “12″,”1212″,”121212″,”12121212″…。上式中12 以( )括住,所以”+” 所表示的是12,重复出现的也是12。

    | 表示逻辑上的”或”(or) 。例如:Regexp/ Oranges?|apples?|water/可用来表示:字符串  “Orange”,”Oranges” 或 “apple”,”apples”  或 “water” 。

     match是什么? 讨论Regexp时,经常遇到”某字符串匹配( match )某Regexp”的字眼。意思是: “这个Regexp可被解释成该字符串”。[ 例如] : 字符串”the” 匹配(match) Regexp /[Tt]he/。因为 Regexp /[Tt]he/ 可解释成字符串 “the” 或 “The”,故字符串 “the”  或 “The”都匹配(match) Regexp /[Th]he/。

      awk 中提供二个关系运算符(Relational Operator):~ ,!~,它们也称之为match,not match。但函义与一般常说的match略有不同。其定义如下:A  表一字符串,B 表一Regular Expression,只要A 字符串中存在有子字符串可match( 一般定义的 match) Regexp B ,则A ~B 就算成立,其值为true,反之则为false。! ~ 的定义与~恰好相反。例如:”another” 中含有子字符串 “the” 可match Regexp /[Tt]he/,所以 “another” ~ /[Tt]he/ 之值为 true

    有些地方不把( ~,!~)与Relational  Operators 归为一类。

     应用Regular Expression 解题的简例:下面列出一些应用Regular Expression 的简例
    例1:将文件中所有的字符串 “Regular Expression” 或 “Regular  expression” 换成 “Regexp” 
[root@myfreelinux pub]# cat regexp
Regular expression
Regular Expression
[root@myfreelinux pub]# awk ‘{gsub(/Regular[ /t]+[eE]xpression/,”Regexp”);print $0;}’ regexp
Regexp
Regexp
    例2:去除文件中的空白行(或仅含空白字符或tab 的行)
[root@myfreelinux pub]# cat regexp
Regular expression
   
Regular Expression
[root@myfreelinux pub]# awk ‘{if($0!~/^[ /t]+$/) print $0;}’ regexp
Regular expression
Regular Expression
     例3:在文件中具有ddd-dddd (电话号码型态,d 表digital)的字符串前加上”TEL :
[root@myfreelinux pub]# cat regexp
83786550
83786450
[root@myfreelinux pub]# awk ‘{gsub(/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/,”TEL: &”);print $0;}’ regexp
TEL: 83786550
TEL: 83786450 
    例4:从文件的Fullname 中分离出路径与文件名
 [root@myfreelinux pub]# awk ‘BEGIN{Fullname=”/usr/local/apache2/bin/apachectl”;match(Fullname,/.*///);path=substr(Fullname,1,RLENGTH-1);filename=substr(Fullname,RLENGTH+1);print Fullname;print path;print filename}’
/usr/local/apache2/bin/apachectl
/usr/local/apache2/bin
apachectl
    例5:将某一数值改以现金表示法表示(整数部分每三位加一撇,且含二位小数)
 [root@myfreelinux pub]# awk ‘BEGIN{number=1234567890;number=sprintf(“¥%.2f”,number); while(match(number,/[0-9][0-9][0-9][0-9]/)) sub(/[0-9][0-9][0-9][.,]/,”,&”,number); print number}’
结果输出 ¥1,234,567,890.00
      例6: 把文件中所有具”program数字.f”形态的字符串改为”[Ref :program数字.c]”
[root@myfreelinux pub]# cat program
program1.f
program2.f
program34.f
program67.f
[root@myfreelinux pub]# awk ‘{while(match($0,/program[0-9]+/.f/)){replace=”[Ref: "substr($0,RSTART,RLENGTH-2)".c]“; sub(/program[0-9]+/.f/,replace);} print $0}’ program
[Ref: program1.c]
[Ref: program2.c]
[Ref: program34.c]
[Ref: program67.c]
解 释说明:以program1.f为例,while(match($0,/program[0-9]+/.f/))是匹配以下”program1.f”的文 件名,匹配后结果会保存到RSTART=0和RLENGTH=10中;substr($0,RSTART,RLENGTH-2)就是去 program1.f的前8位,即program1;replace=”[Ref: "substr($0,RSTART,RLENGTH-2)".c]“的结果就是replace=“[Ref: program1.c]“;sub函数是用来进行代替的,也就是用[Ref: program1.c]代替program1.f。所以输出结果为上式的值。

如果文件a中包含文件b,则将文件b的记录打印出来输出到c文件里

文件a: 
10/05766798607,11/20050325191329,29/0.1,14/05766798607 
10/05767158557,11/20050325191329,29/0.08,14/05767158557 

文件 b: 
05766798607 
05766798608 
05766798609 
通过文件a和文件b对比,导出这样的 文件出来. 
10/05766798607,11/20050325191329,29/0.1,14/05766798607

本人查了很多网上的答案都是错误码的

正确答案应该:

方法一: awk -F'[/,]' 'ARGIND==1{a[$0]}ARGIND>1{if ($2 in a)print $0}' b a >c

方法二: awk -F'[/,]' 'NR==FNR{a[$0]}NR>FNR{if ($2 in a) print $0}' b a >c

这两种方法是用数组处理的,速度比较快,处理9万行只需4秒。

还有一种方法是通过while 每次用read 命令从b中读一条记录与a中$2比较如果相等则输出到c中

root@TestAs4 zlwt]# more for3.sh 
#!/bin/bash
while read line ; do
awk -F'[/,]' '$2 == '$line' {print $0}' a >>c

done < b;

这种方法很好理解,但速度非常慢,每次只读取一条记录,9万行需5个小时处理。

例二 awk数组处理两个文件索引的问题(替代法)

[root@TestAs4 zlwt]# more a
deptA
deptB
deptC
deptD
[root@TestAs4 zlwt]# more b
aaa 0
bbb 1
ccc 2
ddd 0
eee 2
fff 2
[root@TestAs4 zlwt]# awk 'NR==FNR {k[i++]=$1} NR>FNR { print $1,k[$2]}' a b
aaa deptA
bbb deptB
ccc deptC
ddd deptA
eee deptC
fff deptC

NR==FNR {k[i++]=$1} #先把a文件的值赋给数组k,下标从0自动增长

NR>FNR { print $1,k[$2] #其中 $1,$2是b中的第一,二个域,k[$2]为a的值

下面方法是r2007版主的其实是一样的

[root@TestAs4 zlwt]# awk '{if(NR==FNR)k[i++]=$0;else print $1,k[$2]}' a b
aaa deptA
bbb deptB
ccc deptC
ddd deptA
eee deptC
fff deptC

另外一个例子

awk ' BEGIN{FS="[|]";OFS="|"}
FNR==NR{a[$1]=$2}
FNR<NR{if(!a[$1]) {$1="13";print}
else {$1=a[$1];print}}
' wj wj1>wj2



文件1 
1|name1
2|name2
3|name3
5|name5
6|name6

文 件2
1|name11
2|name22
3|name33
4|name44
5|name55
6|name66
7|name77
8|name88

输 出结果
name1|name11
name2|name22
name3|name33
13|name44
name5|name55
name6|name66
13|name77
13|name88

它在处理2个以|分割的文件
例如
文件1 wj 格式
id1|desc1
文件2 wj1格式
id2|desc2

FNR==NR{a[$1]=$2} 意思是处理第一个文件时 把 desc1 赋值给 数组 a 的 a[id1] 单元。
FNR<NR 条件是在处理第2文件成立。这样在处理第2 文件时
{if(!a[$1]) {$1="13";print}
else {$1=a[$1];print
如果a[$1] 是空,就把第2文件那行的第1列替换为 13 输出 如: 13|desc2
如果a[$1]非空,就是这个数组值已经在处理第1文件赋过值。就把$1替换为 a[$1] 即 文件1对应的$2。输出的就是 desc1|desc2

归纳一句 就是在文件2中以id2在文件1中查id1=id2的对应desc1 ,
找 到输出 desc1|desc2
找不到输出 13|desc2

例:把数组中如1331131***** 批量替换成861331131*****

#cat a.txt

13994623***
13394660***
13394660***
13394671***
13394672***
13394690***
13394692***
15304863***

#awk '{print "86"$1}' a.txt > b.txt

8613994623***

8613394660***
8613394660***
8613394671***
8613394672***
8613394690***
8613394692***
8615304863***

#awk '{print substr($1,3,11)}' b.txt    把86去掉
13994623***
13394660***
13394660***
13394671***
13394672***
13394690***
13394692***
15304863***

------------------------------------------------------------------------------
                两个文件关联处理
[root@TestAs4 cwm]# awk '{print $1}' 153mdn.txt |uniq -c        
      4 七台河
      5 伊春
     18 佳木斯
     13 双鸭山
     66 哈尔滨
      1 大兴安岭
     32 大庆
     20 牡丹江
     19 绥化
     16 鸡西
     15 鹤岗
     10 黑河
     19 齐齐哈尔
[root@TestAs4 cwm]# awk '{print $1,substr($1,1,7)}' hlj_jifei >hlj_temp
[root@TestAs4 mdn]# more hlj_temp
13009700055 1300970
13009700495 1300970
13009701075 1300970
13009701282 1300970


[root@TestAs4 mdn]# ls
2 3 awk_script cwm hlj_jifei hlj_temp newmdn_table.TXT temp test1
[root@TestAs4 mdn]# more test1
1300019                 510             020     广州
1300101                 110             010     北京
1300103                 110             010     北京
1300104                 110             010     北京
1300106                 110             010     北京

[root@TestAs4 mdn]# awk 'NR==FNR{a[substr($1,1,7)]=$4}NR>FNR&&a[b=substr($1,1,7)]{print $1,a[b]}' test1 hlj_temp |more

[root@TestAs4 mdn]# awk 'NR==FNR{a[$1]=$4}NR>FNR&&a[b=substr($1,1,7)]{print $1,a[b]}' test1 hlj_temp
13009700055 哈尔滨
13009700495 哈尔滨
13009701075 哈尔滨
13009701282 哈尔滨

--------------------------------------------------------------------------------------
[root@TestAs4 mdn]# more temp 
1300970 13009700055
1300970 13009700495
1300970 13009701075
1300970 13009701282


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

[root@TestAs4 mdn]# more awk_script
BEGIN { while ((getline < "test1") > 0){ lines[$1]=$4 };OFS=" " } 
{ 
   if($1 in lines){ 
       $1=lines[$1]    #把test1文件的$4替换到temp文件的$1上
        print $0
   }
} 
#要求把test1文件的第四个字段插入到temp文件的相应条目的第一个子段中
#利用getline获取test1文件的第四个字 段,并且放到一个数组中。


[root@TestAs4 mdn]# ls
2 3 awk_script cwm hlj_jifei hlj_temp newmdn_table.TXT temp test1
[root@TestAs4 mdn]# awk -f awk_script temp |wc -l
63440
[root@TestAs4 mdn]# awk -f awk_script temp |more
哈尔滨 13009700055
哈尔滨 13009700495

awk又 一个例子: 统计某一列所有值的和

把所有第二列的值求和
[root@TestAs4 ~]# more cwm.txt 
cwm 123
zbl 124
yhh 2
cj   1
[root@TestAs4 ~]# awk '{a[x++]=$2};END{for(i=1; i<=NR; i++)   b=b+a[i-1];print b }' cwm.txt
250
[root@TestAs4 ~]# awk '{a[NR]=$2;b=0};END{for(i=1; i<=NR; i++) b=b+a[i];print b }' cwm.txt
250

显示文件的从第 m行到n行

[root@TestAs4 ~]# sed -n '2,10'p   mdn.txt 

[root@TestAs4 ~]# awk 'NR==2,NR==10{print $0}' mdn.txt 


给手机号码分G网C网
1.C 网(C网是133或153开头的号)

awk '$1 ~/^133/ || $1 ~/^153/'   file.txt >C网.txt

2.G网(由于G网比较多非133非153开头的都是)

awk '$1 !~/^133/ && $1 !~/^153/' file.txt >G网.txt

给 两个文件每行对应连接

[root@TestAs4 cwm]# more tep_01.txt 
cwm    13911320988
zbl    13931095233
chen   12333333333
cwm    12233333333
cwm    45555555555
[root@TestAs4 cwm]# more tep_02.txt 
cwm1    111320988
zbl1    131095233
chen1   133333333
cwm1    133333333
cwm1    455555555

awk 'NR==FNR {a[FNR]=$0} NR>FNR { print $0,a[FNR]}' tep_01.txt tep_02.txt   

cwm1    111320988 cwm    13911320988
zbl1    131095233 zbl    13931095233
chen1   133333333 chen   12333333333
cwm1    133333333 cwm    12233333333
cwm1    455555555 cwm    45555555555

还有一个命令 paste
[root@TestAs4 cwm]# paste tep_01.txt tep_02.txt 
cwm    13911320988      cwm1    111320988
zbl    13931095233      zbl1    131095233
chen   12333333333      chen1   133333333
cwm    12233333333      cwm1    133333333
cwm    45555555555      cwm1    455555555

awk 处理HAN开头下一个HAN的上一行数字为结尾的文件 ... 或者中提取任一文件段 以HAN开头,下一个HAN的上一行数字段为结尾的一段 生成HAN1等这样的文件 
[root@TestAs4 cwm]# more file1.txt 
HAN 1
12 23 34 45
23 45 56
HAN 2
12 23 34 45
23 45 56
12 23 34 45
HAN 3
12 23 34 45
23 45 56 44
12 23 34 45
23 45 56
HAN 4
12 23 34 45
23 45 56
HAN n
awk '{ if ($1=="HAN" && NF==2) fn=$2;   print $0>>"HAN" fn;}' file1.txt 
awk '{fn=$2; print $1 >>fn"hb"}' hbuse.txt 这是所有记录以$2归类。

-----------------------找 出两文件相同及不同的值----------------------------------
awk 'NR==FNR{a[$0]++} NR>FNR&&!a[$0]' file1 file2   找出文件2中不同的值
awk 'NR==FNR{a[$0]++} NR>FNR&&a[$0]' file1 file2   找出两文件中相同的值

awk 'NR==FNR{a[$0]}NR>FNR{ if(!($1 in a)) print $0}' file1 file2 找出文件2中不同的值
awk 'NR==FNR{a[$0]}NR>FNR{ if($1 in a)    print $0}' file1 file2 找出两文件中相同的值
------------------------awk按 字段分类统计----------------------------------------
1300018   广东
1300019   广东
1300100   北京
1300101   北京
1300126   北京
1300127   北京
1300128   北京
1300129   北京
1300130   天津
1300131   天津
1300132   天津
1300133   天津

想得到三个文件:
广东2.txt
1300018 
1300019

北京6.txt
1300100
1300101
1300126
1300127
1300128
1300129
   
天津4.txt
1300130
1300131
1300132
1300133

awk '{a[$2]++;print $1 > $2} END {for (i in a) {print "mv " i " " i""a[i]".txt" }}' ufile|sh

  1. $ cat  file1
  2. 23  中西
  3. 98  红
  4. 34  西 瓜
  5. 53  巴巴
  1. $ cat  file2
  2. 巴巴    c
  3. 红      b
  4. 西 瓜    d
  5. 中西    f

得到

  1. 23  f
  2. 98  b
  3. 34  d
  4. 53  c
  1. awk 'NR==FNR{a[$1]=$2}NR>FNR{print $1,a[$2]}' file2 file1g了一下,

明白是awk是顺序处理file1、file2、file3...
所以新手来解释下高手ywlscpl代码

 

  1. awk 'NR==FNR{a[$1]=$2}NR>FNR{print $1,a[$2]}' file2 file1

1、NR=已处理的记录数;FNR= 当前文件处理的记录数,明确了这个,那么处理第一个文件时,NR是等于FNR的,处理第二个文件时,NR>FNR
2、所以高手 ywlscpl的代码处理第一个文件file2时,只是数组赋值,因为此时NR没有>FNR,即为:
a[巴巴]=c
a[红]=b
a[西 瓜]=d
a[中西]=f
3、继续处理第二个文件file1,这时满足NR>FNR的判断条件,所以打印print $1,a[$2],即为:
print 23,a[中西],a[中西]=d,所以输出是23,f
print 98,a[红],a[红]=b,所以输出是98,b
....................................................34,d
.....................................................53,c

关于awk的多文件处理:

awk的数据输入有两个来源,标准输入和文件,后一种方式支持多个 文件,如
1、 shell的Pathname Expansion方式:awk '{...}'  *.txt      #  *.txt先被shell解释,替换成当前目录下的所有*.txt,如当前目录有1.txt和2.txt,则命令最终为awk '{...}' 1.txt 2.txt
2、直接指定多个文件: awk '{...}' a.txt b.txt c.txt ...
awk对多文件的 处理流程是,依次读取各个文件内容,如上例,先读a.txt,再读b.txt....

那么,在多文件处理的时候,如何判断awk目前读的 是哪个文件,而依次做对应的操作呢?
1、当awk读取的文件只有两个的时候,比较常用的有两种方法
一种是awk 'NR==FNR{...}NR>FNR{...}'  file1 file2   或awk 'NR==FNR{...}NR!=FNR{...}' file1 file2
另一种是 awk 'NR==FNR{...;next}{...}' file1 file2
了解了FNR和NR这两个awk内置变量的意义就很容易知道这两种方 法是如何运作的

QUOTE:
FNR         The input record number in the current input file.       #已读入当前文件的记录数
NR          The total number of input records seen so far.            #已读入的总记录数
       next                  Stop processing the current input record.  The next input record  is
                             read  and  processing  starts over with the first pattern in the AWK
                             program.  If the end of the input data is reached, the END block(s),
                             if any, are executed.


对于awk 'NR==FNR{...}NR>FNR{...}'  file1 file2
读 入file1的时候,已读入file1的记录数FNR一定等于awk已读入的总记录数NR,因为file1是awk读入的首个文件,故读入file1时执 行前一个命令块{...}
读入file2的时候,已读入的总记录数NR一定>读入file2的记录数FNR,故读入file2时执行后一个 命令块{...}
对于awk 'NR==FNR{...;next}{...}' file1 file2
读入file1时,满 足NR==FNR,先执行前一个命令块,但因为其中有next命令,故后一个命令块{...}是不会执行的
读入file2时,不满足 NR==FNR,前一个命令块{..}不会执行,只执行后一个命令块{...}
2、当awk处理的文件超过两个时,显然上面那种方法就不 适用了。因为读第3个文件或以上时,也满足NR>FNR (NR!=FNR),显然无法区分开来。
所以就要用到更通用的方法了:
1、 ARGIND 当前被处理参数标志: awk 'ARGIND==1{...}ARGIND==2{...}ARGIND==3{...}... ' file1 file2 file3 ...
2、ARGV 命令行参数数组:   awk 'FILENAME==ARGV[1]{...}FILENAME==ARGV[2]{...}FILENAME==ARGV[3]{...}...' file1 file2 file3 ...    
3、 把文件名直接加入判断: awk 'FILENAME=="file1"{...}FILENAME=="file2"{...}FILENAME=="file3"{...}...' file1 file2 file3 ...           #没有前两种通用

例:显示文本文件myfile 中第七行到第十五行中以字符%分隔的第一字段,第三字段和第
七字段:
awk -F % 'NR==7,NR==15 {printf $1 $3 $7}'

例:显示文件myfile 中的行号和第1字段:
$awk '{printf"%03d%s\n",NR,$1}' myfile

例:显示文本文件 mydoc 匹配(含有)字符串"sun"的所有行。
$awk '/sun/{print}' mydoc

例:下面是一个较为复杂的匹配的示例:
$awk '/[Ss]un/,/[Mm]oon/ {print}' myfile
它将显示第一个匹配Sun 或sun 的行与第一个匹配Moon 或moon 的行之间的行,并显示到标准输出上。
例:下面的示例显示了内置变量和内置函数 length()的使用:
$awk 'length($0)>80 {print NR}' myfile
该命令行将显示文本myfile 中所有超过80 个字符的行号,在这里,用$0 表示整个记录(行),同时,内置变量NR 不使用标志符'$'。

awk 中允许进行多种测试,如常用的==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)等 等,同时,作为样式匹配,还提供了~(匹配于)和!~(不匹配于)判断,awk 也支持用逻辑运算符:!(非)、&&(与)、||(或)和括号()进行多重判断,这大大增强了awk 的功能。

转载于:https://www.cnblogs.com/flyoo/archive/2013/04/09/3009623.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值