因为自己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:BEGIN
和 END
.
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倒序排列.
网友评论