awk 应用实例

awk 应用实例

------------------------------------------------------------
author:hjjdebug
date:  Thu Jun 12 14:01:53 CST 2014
------------------------------------------------------------
1. 保存数据, 百变输出
------------------------------------------------------------
输入数据:
[hjj@Dell ~]$ cat input.txt
goodsnum:  5  6  8  3  9  8  2
price:     15 16 18 13 19 18 12

awk 处理:
[hjj@Dell ~]$ cat read.awk
{
    for(i=2;i<=NF; i++){ #这里忽略第1列,从第2列开始存储
        if($0~/^goodsnum/)goodsnum[i]=$i
        if($0~/^price/)price[i]=$i
    }
}
END{
    for(i=2;i<=NF;i++) #数组下标0,1 未用
        print "item"i-1, "goodsnum:"goodsnum[i], "price:"price[i]
}
结果输出:
[hjj@hjj ~]$ cat input.txt |awk -f read.awk
item1 goodsnum:5 price:15
item2 goodsnum:6 price:16
item3 goodsnum:8 price:18
item4 goodsnum:3 price:13
item5 goodsnum:9 price:19
item6 goodsnum:8 price:18
item7 goodsnum:2 price:12

这里演示了向数组赋值的一种方法,将字段值直接赋值给下标数组变量
------------------------------------------------------------
2. 强力搜索, 过滤输出
------------------------------------------------------------
输入数据:
[hjj@hjj ~]$ cat data.txt
01-04 00:50:44.004 V/stbca   (  233): wReplyLen: 61 2F
01-04 00:50:44.214 V/stbca   (  233): pbyOddKey: 29 AD 9C 72 58 3A 2B BD
01-04 00:50:44.814 V/stbca   (  233): pDummyKey: 39 AD 3C A2 38 3A 5B 6D
要求: 过滤输出,
过滤条件:  过滤出带8个数据的行,并且(byte1+byte2+byte3) & 0xFF = byte4
第一行: 有效数据个数不够,不过滤
第二行: 过滤出. 满足条件: (0x29+0xAD+0x9C) & 0xFF = 0x72  0x72 == 0x72(byte4)
第三行: 不满足sum条件, 不过滤 (0x39+0xAD+0x3C) & 0xFF = 0x22  0x22!=0xA2(byte4)

强大的正则表达式,但grep 只能止步于此。不能满足第二个条件
[hjj@hjj ~]$ cat data.txt |grep -E --color "([0-9A-F]{2} ){7,}"
01-04 00:50:44.214 V/stbca   (  233): pbyOddKey: 29 AD 9C 72 58 3A 2B BD
01-04 00:50:44.814 V/stbca   (  233): pDummyKey: 39 AD 3C A2 38 3A 5B 6D

awk 方案: 技巧都在代码里
[hjj@hjj ~]$ cat search.awk
{
    if(match($0,/([0-9A-F]{2} ){7,}/)) {
        str=substr($0,RSTART,RLENGTH) # match 提供了RSTART, RLENGTH
#       print str
        split(str,a," ")
#       print a[1]
        sum=strtonum("0x"a[1])+strtonum("0x"a[2])+strtonum("0x"a[3])
#       printf "%x\n", sum;
        if(and(sum,0xff) == strtonum("0x"a[4])) print;  # bit 与书写确实有点烦
    }
}

输出结果:
因为是扩展正则匹配,所以一定要加--re-interval, 否则匹配不到,无输出。
[hjj@hjj ~]$ cat data.txt |awk -f search.awk
[hjj@hjj ~]$ cat data.txt |awk --re-interval -f search.awk
01-04 00:50:44.214 V/stbca   (  233): pbyOddKey: 29 AD 9C 72 58 3A 2B BD

这里演示了另一种数组赋值方法:用split 把字符串直接分割到数组,数组下标是从1开始的。
--------------------------------------------------------------------------------
3. 提取时间信息
--------------------------------------------------------------------------------
a. 先看结果
[hjj@hjj ~]$ echo "01-04 00:50:44.004 V/stbca" |awk -f time.awk
00时 50分 44秒
时间: 3044 秒

b. 再看代码
[hjj@hjj ~]$ cat time.awk
func time(s)
{
    split(s,a,":")  # split 数组下标从1开始
    print a[1]"时",a[2]"分", a[3]"秒"
    res=a[1]*3600 + a[2]*60 + a[3]
    return res
}
{
    if(match($0,/[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/))
    {
        str=substr($0,RSTART,RLENGTH);
        t=time(str)
        print "时间:", t, "秒"
    }

}

注意: awk 中,只有函数参数是局部变量,函数内声明的变量仍然是全局变量。

仍然采用的是match,substr,split 方式把数据提取到数组。

-------------------------------------------------------------------------------
4. 将16进制字符流输出为字符串
--------------------------------------------------------------------------------
[hjj@hjj ~]$ echo  46464952 |awk '{for(i=1;i<length($0);i+=2) printf "%c", strtonum("0x"substr($0,i,2))}'
FFIR

echo "6c 65 6e 3a 32 30 30 00" |awk  '{for(i=1;i<=NF;i++) printf("%c", strtonum("0x"$i))}'

len:200

应用: 统计及计算, 给出结果。

$ cat tj.awk
{
        if($need~/[0-9.]+$/)
        {
                if($need>9.8 && $need < 10)
                {
                        n=10
                }
                else {
                        n=int($need)
                }
                a[n]++;
                num++
        }
        else if($need ~ "%")
        {
                print $need
        }
}
END {
        for(i in a)
        {
                r=sprintf("%.2f", a[i]*100/num );
                print i "\t" a[i] "\t" r "%";
        }
}
使用:

$ awk -f tj.awk need=4 z.txt |sort -n
-5      3       0.13%
-4      2       0.09%
-3      4       0.17%
-2      6       0.26%
-1      72      3.10%
0       929     39.96%
name%%
1       671     28.86%
2       310     13.33%
3       145     6.24%
4       69      2.97%
5       38      1.63%
6       13      0.56%
7       13      0.56%
8       10      0.43%
10      40      1.72%

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

5. 阶乘的计算

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

[hjj@hjj ~]$ cat fac.awk
#!/bin/awk
function factorial(n,t,s)
{
    s=1;

    for (t=1; t<=n; t++)
    {
        s *= t;
    }

    return s;
}

BEGIN {
    for (i=1; i<=10; i++)
    {
        value = factorial(i);
        printf("fac(%d) = %d\n", i, value);
    }
}
$ awk -f fac.awk --dump-variable=1.tmp

[hjj@hjj ~]$ cat 1.tmp
ARGC: number (1)
ARGIND: number (0)
ARGV: array, 1 elements
BINMODE: number (0)
CONVFMT: string ("%.6g")
ERRNO: number (0)
FIELDWIDTHS: string ("")
FILENAME: string ("-")
FNR: number (1)
FS: string (" ")
IGNORECASE: number (0)
LINT: number (0)
NF: number (0)
NR: number (1)
OFMT: string ("%.6g")
OFS: string (" ")
ORS: string ("\n")
RLENGTH: number (0)
RS: string ("\n")
RSTART: number (0)
RT: string ("\n")
SUBSEP: string ("\034")
TEXTDOMAIN: string ("messages")
i: number (11)
value: number (3628800)


前面是系统变量,后面两个是全局变量

----------------------------------------
6. awk 查询字符ascii 值
----------------------------------------
awk 由于不支持'A' 之类的字符类型,所以不能直接使用print, printf之类。
但是它支持关联数组,下面的方法竟是先造出来一个ascii数组,
这个数组以字符为下标。字符的值为数组值。
把输入的字符串分割为字符,再打印这个ascii数组的值。
$echo "ABCabc"|awk -F "" 'BEGIN{for(i=0;i<255;i++)a[sprintf("%c",i)]=i}{for(i=1;i<=NF;i++)print a[$i]}'
65
66
67
97
98
99

$1, $2, $... 分别代表第一列内容,第二列内容,第...列内容, 内容就是字母或者数字.

sprintf 的返回值仍然是一个字符串.

技巧:先造出来一个256个元素的数组,其下标是256个字符,其值是ascii数值(就是顺序号),
i: 是索引
$i:是字段, 实际由单个字符构成
a[$i]: 是该字符对应的ascii值


7. awk 判别行尾是否为回车符
----------------------------------------

 echo -e "abc\r\n" |awk '{d=substr($0,length($0));if(d=="\r") print "tail is carriage";}'
 

由于awk 没有字符的概念,所以用子串来进行判别. 当你能用awk处理字符类型问题时,感觉就前进了一步.

写此脚本起因是用awk处理一个dos文件时, 曾用print 向新文件输出内容.结果用vim打开看,发现很多^M

原因是dos文件以\r\n为回车, 输出原文件行,每行尾会有一个0x0d 回车符号.

去掉^M 的方法:

第一种方法: 对原文件的行都删掉最后一个字符,用awk '{sub(/.$/,"")} 但感觉粗暴了一些,

第二种方法:先判断一下最后一个字符是否为回车0xd. awk的数组下标从1开始(与c语言不同的地方)

if(substr($0,length($0)=="\r") $0=substr($0,1,length($0)-1)

第三种方法: 还保留原来的dos格式输出. 就是你新添加的行,也要以\r\n来结尾, 这只需要你在print 是, 加上"\r"

  就可以了,

总结: 出现很多^M 的原因是,有的行以\r\n结尾,有的行以\n结尾, 所以显示时, 按unit格式显示,就多出了很多^M(回车符号)

而如果全是\r\n,则为dos格式,不会有^M出现! 用awk 可以挑选你最喜欢的办法去处理, 建议第3种.

技巧:awk没有字符的概念,只有字符串的概念,故用取子字符串方式来比较.

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

8. awk 的格式化输出 printf

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

例如:

 lspci |awk -F: '{printf "%-5s:%-35s:%s\n", $1,$2,$3}'

---------------------------------------------
9. 动态调整打印宽度: printf "%*s",width,str
---------------------------------------------
awk 脚本文件: cat 2.awk
#!/bin/awk -f
BEGIN     { OFS = FS = "|" } #设定输入和输出字段分割符,命令行中只能设置输入分界符,设不了输出分界符,所以一块在这设置了
FNR == NR { for (i=1; i<=NF; ++i) if (m[i] < (n=length($i))+1) m[i] = n+1; next} #next 跳过了后面的语句,该语句用来生成m[i]数组
{ for (i=1; i<=NF; ++i) $i = sprintf("%d %-*s", m[i], m[i], $i); print } #%* 为可变宽度(或动态宽度),其宽度值由m[i]确定

测试文件: cat 1.txt
c1|c2|c3|c4|c5
c6|c7|c8|c9|c10
c11|c12|c13|c14|c15

执行:
$ awk -f 2.awk 1.txt 1.txt
配合2.awk, 对输入文件2次扫描,第一遍获取各字段宽度信息,第二遍输出.
输出结果:

 awk -f 2.awk 1.txt 1.txt
4 c1  |4 c2  |4 c3  |4 c4  |4 c5  
4 c6  |4 c7  |4 c8  |4 c9  |4 c10
4 c11 |4 c12 |4 c13 |4 c14 |4 c15

-------------------------------------------------------
10. 用awk 对数组排序输出,(可以通过过滤用外部的sort命令)
-------------------------------------------------------
例: cat 1.awk
#!/bin/awk -f
BEGIN {
    data[1]=100;
    data[3]=200;
    data[5]=300;
    for(i in data)
    {
        print i,data[i]|"sort -k1 -n -r"  # 可以通过过滤器调用外部应用程序实现排序
    } # 至于如何从外部程序获取到输入, 嗯...$1,$2,$3..., 如果从过滤中读回? 嗯...
}
执行: $ awk -f 1.awk
5 300
3 200
1 100

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值