免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 19592 | 回复: 14

发个自己写的AWK简单教程 [复制链接]

论坛徽章:
0
发表于 2010-10-11 17:20 |显示全部楼层
[i=s] 本帖最后由 lovelyarry 于 2010-10-11 17:22 编辑 [/i]

〇、前言
        意见反馈,请mailto:datouwang...at...gmail.com。

一、AWK简介
        AWK名字来源于三位创造者Aho、Weinberger和Kernighan统称。
        AWK擅长处理文本数据。

二、AWK的调用方式
        awk [-Ffs] [-v var=value] [program | -f progfile ...] [file ...]

        1、命令行方式
        例如:
       
awk '{print $1}' file
ps -ef|grep program|awk '{print $2}'


        2、文件方式
        例如:
awk -f progfile file

       
        3、文件解释器方式
        AWK脚本文件开头需要注明调用方式,典型写法为:
        #!/bin/awk -f
        注意-f后面有空格。
        脚本文件需要有执行权限,如果没有需要使用chmod +x progfile赋权。
        例如:
progfile file

三、AWK参数
        -F        指定域分隔符,例如:-F "|",即以|作为域分隔符,默认分隔符为一个或多个空格或TAB,即"[[:space:]][[:space:]]*"。
        -v        定义变量,从shell给awk传递变量,如-vDATE=$DATE,即将shell中$DATE变量值传递给awk变量DATE。
        -f        指定脚本文件,例如-f progfile。

四、AWK内置变量
        FS                域分隔符
        NF                域个数
        NR                行数
        FNR                同上
        FILENAME        处理的文件名,当输入为管道时,FILENAME为空。
        RS                行分隔符
        OFS                输出域分隔符
        ORS                输出行分隔符
        OFMT                数字输出格式
        CONVFMT                数字内部转换格式
        SUBSEP                多维数组索引分隔符
        ARGC                输入参数个数
        ARGV                输入参数数组
        ENVIRON                环境变量数组
        RSTART                match()函数正则匹配到字符串开始位置
        RLENGTH                match()函数正则匹配到字符串的长度

五、AWK内置函数
        blength[([s])]                        计算字符串长度(byte为单位)
        length[([s])]                        计算字符串长度(character为单位)
        rand()                                生成随机数
        srand([expr])                        设置rand() seed
        int(x)                                字符串转换为整型
        substr(s, m [, n])                取子字符串
        index(s, t)                        在字符串s中定位t字符串首次出现的位置
        match(s, ere)                        在字符串s中匹配正则ere,match修改RSTART、RLENGTH变量。
        split(s, a[, fs])                将字符串分割到数组中
        sub(ere, repl [, in])        字符串替换
        gsub                                同上
        sprintf(fmt, expr, ...)        拼字符串
        system(cmd)                        在shell中执行cmd。
        toupper(s)                        字符串转换为大写
        tolower(s)                        字符串转换为小写


六、AWK流程控制
        if(expression) statement [ else statement ]
        while(expression) statement
        for(expression;expression;expression) statement
        for(var in array) statement
        do statement while(expression)
        break
        continue
        {[statement  ...]}
        expression          # commonly  var = expression
        print [expression-list] [ > expression]
        printf format [, expression-list] [ > expression]
        return [expression]
        next                # skip remaining patterns on this input line.
        delete array [expression]   # delete an array element.
        exit [expression]   # exit immediately; status is expression.

七、AWK简单应用范例
        AWK脚本分为三部分BEGIN段,处理段,END段。其中BEGIN段在第一行读取之前执行,END段在最后一行处理后执行。

1、内容过滤,同"grep tag file"。
#前两个语句为正则匹配

awk '/tag/ {print}' file
awk '{if($0 ~/tag/) print}' file
awk '{if(index($0, "tag") > 0) print}' file


2、取特定列,同"cut –f1 –f3 –f5 file"。
#输出文件第1、3、5列

awk '{print $1, $3, $5}' file

       
3、对文件内容进行剔重,类似"sort -u file",但未排序。
#如果当前行未存在于rec HASH表中,则记录此行数据,并输出

awk '{if(!($0 in rec)) {rec[$0]=1; print $0;}}' file

        AWK中数组有两种用法普通数组和HASH数组,此处为HASH数组。
       

4、仅输出数据
#输出100行数据

awk ‘BEGIN {for(i = 0; i < 100; i++) printf("this is %d\n", i);}’

可见,如果脚本中只有BEGIN段,可以没有输入。

5、统计数据
#对第一列和第二列数据进行汇总,最终输出

awk ‘{a+=$1; b+=$2}END{printf("a=%d\n,b=%d\n", a, b);}’ file



八、AWK高级应用范例

1、        分组功能,类似Group by功能
#使用第一列作为分组列,第二列为聚合列,即select col1, sum(col2) from file group by col1

awk ‘{tot[$1] += $2}END{for(i in tot) printf("%s %d\n", i, tot[i]);}’ file

#比上个例子增加一个类似having的用法

awk ‘{tot[$1] += $2}END{for(i in tot) if(tot[i] > 10) printf("%s %d\n", i, tot[i]);}’ file

#使用第一列作为分组列,第二列、第三列为聚合列

awk ‘{tot1[$1] += $2; tot2[$1] += $3;}END{for(i in tot1) printf("%s %d %d\n", i, tot1[i], tot2[i]);}’ file

#多维数组例子,可将多个字段作为分组列,AWK使用一维数组模拟多维数组,使用\034作为分隔符

awk ‘{tot1[$1, $2] += $3; tot2[$1, $2] += $4;}END{for(i in tot1) printf("%s %d %d\n", i, tot1[i], tot2[i]);}’ file  



2、        文件操作
#将两个文件根据filename1的第一列和filename2的第二列进行关联

BEGIN {
        #读取filename1文件内容
        while((getline < "filename1") != NULL)
        {
                rel[$1] = 1;
                rec1[$1] = $2;
        }

        while((getline < "filename2") != NULL)
        {
                rel[$2] = 1;
                rec2[$2] = $3;
        }

        for(i in rel)
        {
                printf("%s %s %s\n", i, rec1[i], rec2[i]);
        }
}

#将文件按照字段进行拆分

{
        print $0 >> "split/" substr($1,1,7);
}


3、        从SHELL向AWK传递变量
awk  -vAWK_DATE=$DATE 'BEGIN {print AWK_DATE}'


4、        在AWK内部读取shell命令输出
#读取ls命令输出,在AWK中打印输出

BEGIN {
        while("ls"|getline)
        {
                print $0;
        }
}

#读取date输出

BEGIN {
        "date" | getline;

        print $0;

        "date +%Y" | getline v_year;

        print v_year;
}



5、        将AWK输出通过管道传递给SHELL命令
#将打印信息输出给sort进行排序

BEGIN {
        for(i = 0; i < 100; i++)
        {
                printf("%03d\n", 100 - i) | "sort";
        }
}


6、        正则表达式简单例子
#演示正则表达式的使用方法

BEGIN {
        str1 = "abc.123@gmail.com";
        str2 = "123123abcd@gmail.com";
        str3 = "&^%76123@gmail.com";
        str4 = "zxcvb@sohu.com.cn.1231231";

        match(str1, "[a-zA-Z][a-zA-Z0-9.]*@[a-zA-Z0-9][a-zA-Z0-9.]*.[a-zA-Z]*[a-zA-Z]");

        if(RSTART > 0)
                printf("%s\n", substr(str1, RSTART, RLENGTH));
        else
                printf("[%s] not match\n", str1);

        match(str2, "[a-zA-Z][a-zA-Z0-9.]*@[a-zA-Z0-9][a-zA-Z0-9.]*.[a-zA-Z]*[a-zA-Z]");

        if(RSTART > 0)
                printf("%s\n", substr(str2, RSTART, RLENGTH));
        else
                printf("[%s] not match\n", str2);

        match(str3, "[a-zA-Z][a-zA-Z0-9.]*@[a-zA-Z0-9][a-zA-Z0-9.]*.[a-zA-Z]*[a-zA-Z]");

        if(RSTART > 0)
                printf("%s\n", substr(str3, RSTART, RLENGTH));
        else
                printf("[%s] not match\n", str3);

        match(str4, "[a-zA-Z][a-zA-Z0-9.]*@[a-zA-Z0-9][a-zA-Z0-9.]*.[a-zA-Z]*[a-zA-Z]");

        if(RSTART > 0)
                printf("%s\n", substr(str4, RSTART, RLENGTH));
        else
                printf("[%s] not match\n", str4);


}


7、自定义函数
function my_plus(a, b)
{
        return a + b;
}
BEGIN {
        printf("%d\n", my_plus(123, 321));
}


九、一些应用范例
        1、验证话单正确性的一个脚本
/^vc/ {
        #取话单中各个变量
        call_type = substr($0,3,2);
        call_duration = int(substr($0,95,6));
        roam_type = substr($0,210,1);
        fee_type = substr($0,211,1);
        dial_type = substr($0,212,3);
        chat_type = substr($0,215,3);

        cfee = int(substr($0,218,9));
        lfee = int(substr($0,236,9));

        #如果为国际漫游,不分析,跳过
        if(roam_type > 4)
        {
                next;
        }

        if(call_type == "01")
        {
                if(substr(dial_type,1,1) != "0")
                {
                        if(lfee > 0)
                        {
                                printf("%s:LFEE_01\n", $0);
                        }

                        next;
                }

                if(roam_type != "0")
                {
                        if(fee_type == "0" || fee_type == "2" || fee_type == "3")
                        {
                                if(lfee > 0)
                                {
                                        printf("%s:LFEE_ERR02\n", $0)
                                }
                        }
                        else
                        {
                                if(cfee > 0)
                                {
                                        printf("%s:CFEE_ERR01\n", $0);
                                }
                        }
                }
                else
                {
                        if(fee_type != "0")
                        {
                                if(cfee > 0)
                                {
                                        printf("%s:CFEE_ERR02\n", $0);
                                }
                        }
                }
        }

        if(call_type == "02")
        {
                if(lfee > 0)
                {
                        printf("%s:LFEE_ERR03\n", $0);
                }
        }
}

        2、一个模拟求取批价标批费率计划的例子
function my_match(str, pat)
{
#for debug
#printf("str==>|%s|,pat==>|%s|\n", str, pat);

        if(pat == "*")
                return 1;

        n = split(pat, arr, ",");

        for(z = 1; z <= n; z++)
        {
                gsub("\?", "[a-zA-Z0-9]", arr[z]);

#for debug
#printf("str==|%s|,arr==>|%s|\n", str, arr[z]);
               
                match(str, arr[z]);
                if(RSTART > 0)
                {
                        return 1;
                }
        }

        return 0;
        
}

BEGIN {

        dial_cnt = 0;
        while((getline < "dial.lst") != NULL)
        {
                dial[dial_cnt] = $1;

                dial_cnt++;
        }

        chat_cnt = 0;
        while((getline < "chat.lst") != NULL)
        {
                chat[chat_cnt] = $1;

                chat_cnt++;
        }

        cfg_cnt = 0;

        while((getline < "plan.lst") != NULL)
        {
                cfg_dial[cfg_cnt] = $1;
                cfg_chat[cfg_cnt] = $2;
                cfg_item[cfg_cnt] = $3;
                cfg_plan[cfg_cnt] = $4;

                cfg_cnt++;
        }

        for(d = 0; d < dial_cnt; d++)
        {
                for(c = 0; c < chat_cnt; c++)
                {
                        printf("%s %s|", dial[d], chat[c]);
                        
                        out_cnt = 0;
                        
                        for(i = 0; i < cfg_cnt; i++)
                        {
#for debug
#printf("\n<%d,%d,%d>test match==>|<%s, %s>; <%s, %s>|\n", d, c, i, dial[d], cfg_dial[i], chat[c], cfg_chat[i]);

                                if(my_match(dial[d], cfg_dial[i]) == 1 && my_match(chat[c], cfg_chat[i]) == 1)
                                {
                                        if(out_cnt == 0)
                                        {
                                                printf("%s %s %s %s\n", cfg_item[i], cfg_plan[i], cfg_dial[i], cfg_chat[i]);
                                        }
                                        else
                                        {
                                                printf("%s %s|%s %s %s %s\n", dial[d], chat[c], cfg_item[i], cfg_plan[i], cfg_dial[i], cfg_chat[i]);
                                        }
                                                
                                        out_cnt++;
                                }
                        }
                        
                        if(out_cnt == 0)
                        {
                                printf("NULL\n");
                        }
                }
        }
}

评分

参与人数 1可用积分 +5 收起 理由
expert1 + 5 nice job!

查看全部评分

论坛徽章:
0
发表于 2010-10-11 17:33 |显示全部楼层
为啥放弃ed捏?。。。。sed还是很猛的。。。。。

论坛徽章:
0
发表于 2010-10-11 17:35 |显示全部楼层
楼主好人

论坛徽章:
0
发表于 2010-10-11 18:26 |显示全部楼层
支持分享,当手册用,想不起来的时候查一查

论坛徽章:
2
2015年亚洲杯之巴勒斯坦
日期:2015-02-15 12:11:292015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2010-10-11 20:25 |显示全部楼层
学习一下。顶

论坛徽章:
0
发表于 2010-10-11 20:31 |显示全部楼层
多谢分享,继续学习~

论坛徽章:
0
发表于 2010-10-11 21:37 |显示全部楼层
学习 写的很详细。

论坛徽章:
0
发表于 2010-10-11 22:44 |显示全部楼层
不错,全面,谢谢分享

论坛徽章:
0
发表于 2010-12-11 13:39 |显示全部楼层
好东西,顶了

论坛徽章:
0
发表于 2010-12-11 13:59 |显示全部楼层
感谢分享!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP