awk学习笔记

因为自己shell脚本不怎么会就想着刷点题目看看哪里是盲点.然后leetcode上shell分类很少,就四道,前面两道简单的没啥说的,后面两道用到了awk,恰好也是自己不会的,趁着这个机会翻阅资料学习并记录下来以备后面翻阅

一. 概述

awk 是一个强大的文本分析工具.他的工作流程是按行读入文本数据,然后默认以空格分隔切分行数据.然后切分开的部分按照指定的语句进行处理.

其中我们常用的应该是gawk.他是GNU版本的AWK实现.相当于就是一种工具.然后我第一次写是用的mawk,他相当于是awk语言的解释器.这两者我在做题时仅仅遇到了一处不一样的地方,就是对二维数组的支持.

gawk应该是完全支持的,格式如下即可使用:

array_name[i][j]

在mawk中则是以另外一种形式支持二维数组:

array_name[i,j]

这边暂时还是以gawk为主.

二. awk基本说明

这边我个人使用过程中的一个模板是这样的:

awk [-F filed-separator] 'pattern{command}' file-source

其中 -F 指定分隔符,默认是空格.pattern是匹配原则.{}中是处理命令,最后则是数据源

有两个特殊的pattern:BEGINEND.

BEGIN{command}

BEGIN是指在读入数据之前匹配成功,所以BEGIN后接的指令是在读取数据之前执行.可以用来做变量声明以及初始化等等

END{command}

END是指处理完文件后匹配成功.所以后面接的指令是在读取数据完成之后执行的,可用来清理现场或者处理结果

然后有部分内置变量说明下(详细的可去官网查看):

NR 已读取的记录数

NF 记录域的个数

FILENAME 浏览的文件名

针对NF,其中 $n是用来获取对应域的数据的.不过比较不同的是下标从1开始,0表示的是该行全部数据.

一个最简单的例子:
文件file.txt如下

name age
evan 12
gkond 13
awk '/va/{print $2}'  file.txt

这个就是当读入的数据能和va匹配时就输出第二个域.这里只有evan中含有va,所以输出的就是12.

其中awk是支持编程的,整体语句规范类似C.

三. 实例

3.1 转置文件

给定一个文件 file.txt,转置它的内容。

你可以假设每行列数相同,并且每个字段由 ' ' 分隔.

示例:

假设 file.txt 文件内容如下:

name age
alice 21
ryan 30
应当输出:

name alice ryan
age 21 30

这个最直观的一个想法就是创建一个二维数组res[NF][NR],然后按照每个域输出数据即可.

所以具体代码如下:

awk 'BEGIN{
    col=0;row=0;
}
{
    for(i=1;i<=NF;i++){
        res[NR,i]=$i
    }
    col=NF;
    row=NR
}END{
    for(i=1;i<=col;i++){
        for(j=1;j<=row;j++){
            if(j!=row){
                printf("%s ",res[j,i])
            }else{
                 printf("%s\n",res[j,i])
            }
        }
    }
}' file.txt

其中如果使用的是gawk的话,res[j,i]的格式可以写成res[j][i]

上面的代码是为了说明二维数组的使用的,其实还有一种就是一维数组直接存储拼接好的字符串,效率也会更高,代码如下:

awk '{
    for (i = 1; i <= NF; ++i) {
        if (NR == 1) s[i] = $i;
        else s[i] = s[i] " " $i;
    }
} END {
    for (i = 1; s[i] != ""; ++i) {
        print s[i];
    }
}' file.txt

这里代码的意思就是每行进来时,遍历每个域,第i个域就拼到s[i]中,如果是第一行就直接拼接,如果不是第一行就得多加个空格拼接.处理完成之后就是输出了.

3.2 统计词频

写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率。

为了简单起见,你可以假设:

words.txt只包括小写字母和 ' ' 。
每个单词只由小写字母组成。
单词间由一个或多个空格字符分隔。
示例:

假设 words.txt 内容如下:

the day is sunny the the
the sunny is is
你的脚本应当输出(以词频降序排列):

the 4
is 3
sunny 2
day 1
说明:

不要担心词频相同的单词的排序问题,每个单词出现的频率都是唯一的。
你可以使用一行 Unix pipes 实现吗?

这里我看到的最佳答案是通过pipe拼接多个命令完成的:

cat words.txt | tr -s ' ' '\n' | sort | uniq -c | sort -r | awk '{print $2" "$1}'

这里代码的意思是:获取文件内容,然后交给tr做字符替换,空格转换为换行,接着升序排序,再交给uniq做去重并统计,统计的结果在交给sort处理成降序,最后交给awk按照第二个域 第一个域的格式输出.

当然也可以将大部分操作交给awk做:

awk '{
        for(i=1;i<=NF;i++){
            res[$i]+=1;
        }
}END{
        for(i in res){
            print i " " res[i]
        }
}' words.txt | sort -nr -k 2

这里就是用单词做下标,次数做value来存储数据,然后按照第二个pos倒序排列.

你可能感兴趣的:(awk学习笔记)