使用shell+awk完成Hive查询结果格式化输出
好久不写,一方面是工作原因,有些东西没发直接发,另外的也是习惯给丢了,内因所致。今天是个好日子,走起!
btw,实际上这种格式化输出应该不只限于某一种需求,差不多是通用的。
需求:
--基本的:当前Hive查询结果存在数据与表头无法对齐的情况,不便于监控人员直接查看,或者导出到excel中,需要提供一个脚本,将查询结果处理下,便于后续的查看或者操作。
--额外的:A、每次查询出来的结果字段数、字段长度不固定;B、每个数据文件中可能包含不只一套查询结果,即存在多个schema。
想法:
对于基本需求而言,无非就是将数据文件用格式化输出整理一下,直接想到了awk。
对于补充的情况,A:需要实现一种机制,基于数据文件,动态地确定格式化输出的参数:字段个数,以及每个格式化字符串的长度参数;B:实现对数据文件根据字段数切割成多段,然后对于每段数据套用前面的脚本处理。
做法:
基本需求:
1、指定字段分隔符为“\t”
2、将每个字段按照指定长度格式化输出
BEGIN{
FS="\t"
}
{
printf "%-"len"s\t",$i
}
额外需求A:
需要把代码写成“活”的,适应各种不同的数据文件,如前面所说,实际上就是在执行格式化输出之前,将数据文件扫描一遍,用一个数组记录下文件中每个字段的max length,然后将这个max length作为该文件内格式化输出的额定宽度。
1、初始化一个fieldLen数组
2、扫描整个文件,更新fieldLen数组
3、将fieldLen数组,用于格式化输出
BEGIN{
FS="\t"
}
NR=={
for (i=;i<=NF;i++)
fieldLen[i]=
}
{
for (i=;i<=NF;i++)
{
len=length($i)
if (len>fieldLen[i])
{
fieldLen[i]=len
}
}
}
END{
for (i=;i<=NF;i++)
{
printf "%-s",fieldLen[i]
if (i<NF)
printf "\t"
else
printf "\n"
}
}
这里要注意的是,fieldLen的初始化要在NR==1的时候,在BEGIN里面,NF为0
额外需求B:
这里需要一些临时变量,标记分割出来的数据块分支:suffix标记不同的分支,fields当前处理数据块的字段数
处理过程根据前面的临时变量,完成数据文件分割。此处有一个局限在于,对于文件内的多个数据分块,只能处理“AAABBBCCC”这样,同一类数据放在一起的,脚本会分成3块;而对于“AABCABBCC”这种的,则会分割成6块。
BEGIN{
FS="\t"
suffix=
filename=ARGV[]
fields=
}
{
if (NF!=fields)
{
fields=NF
suffix+=
}
print $>filename"."suffix
}
END{
print suffix
}
基本的思路,就如上面所示。
但是,完成上面的部分,可能不到一半的工作量,接下来,说几个比较麻烦的问题:
A、汉字的问题
这个也是对不齐的主要原因。
在putty里面显示的时候,一个汉字占2个字宽,一个ASCII字符占一个字宽。但是,在调用awk内置的length()函数时,一个汉字跟一个ASCII字符长度是一样的。所以为了在putty上看到的内容是对齐的,需要在格式化输出的时候,对fieldLen的值进行修正。
例子如:

如上,计算得到的fieldLen为4,但实际上需要8;但是在printf的时候,为了对齐,从“abs”到“泰国香蕉”printf的len值是不一样的,根据字段情况,动态决定
所以需要修正的有2处:
1、在计算fieldLen的时候,根据汉字情况,将length($i)获取值加上一个变量
for (i=;i<=NF;i++)
{
len=length($i)
for (j=;j<=length($i);j++)
if (substr($i,j,) > "\177")
len+=
if (len>fieldLen[i])
{
fieldLen[i]=len
}
}
2、在printf格式化输出的时候,根据汉字情况,给fieldLen[i]减去一个变量
for (i=;i<=NF;i++)
{ len=
for (j=;j<=length($i);j++)
if (substr($i,j,) > "\177")
len+=
printf "%-'"fieldLen[i]-len"'s",$i if (i<NF)
printf "\t"
else
printf "\n"
}
原理比较简单了,就是前面提到的,汉字比ASCII字符多占一个位置,所以在获取fiedlLen的时候,要加上汉字多占的部分;在格式化输出的时候,汉字要减去多占的部分。
这里用到了一种awk内识别汉字的方法,参考了网上一个同学的帖子:
for (j=;j<=length($i);j++)
if (substr($i,j,) > "\177")
#TODO
原理就是挨个字符进行检测,“\177”是8进制的127,超过127的都算汉字。
B、多文件输入的问题
按照前面的思路,先要扫描一遍,将数据文件的字段信息存下来,然后再引入字段信息和数据文件,做最终的处理。
这里有一个问题是:是否有必要将字段信息保存成单独文件?从awk的原理来看,基本上是一遍扫描,当第一遍扫描完,之后,游标已经到了文件末尾。这样看不太方便在一个awk处理流程中完成对同一个文件的2次扫描。即使有方法,或许也比较复杂,2遍就两遍吧。
awk多文件输入比较简单,但是我们这里的需求是先读取第一个文件的内容,保存到fieldLen数组;然后利用fieldLen数组,处理第二个文件。这里用到的是NR,FNR这两个变量的作用域不同而完成的:NR服务于整个awk处理,FNR服务于某个文件。
NR==FNR{
for (i=;i<=NF;i++)
fieldLen[i]=$i
}
NR!=FNR{
#TODO
}
C、printf变量做字宽的问题
前面一直说,根据数据文件,动态地确定字段宽度,所以到最后一步,格式化输出的时候,%s在指定宽度的时候,需要用一个变量指定宽度。这是一个awk语言了解是否透彻的问题,花费了不短时间才搞定,直接贴代码吧。
printf "%-'"fieldLen[i]-len"'s",$i
D、效率的问题
在脚本执行过程中,出于了处理方便或者逻辑明确的考虑,存在不少的写文件操作。特做如下的测试:
| 文件 | 记录数 | size | 处理时间 |
| a.dat | 642 |
240K |
<1s |
| b.dat |
500000 |
30M |
35s |
| c.dat |
1000000 |
168M |
3min42s |
| combine.dat |
1500642 |
198M |
4min9s |
从实际角度来说,这种格式化的处理,通常数据量不会特别大,同时对实时性要求不那么高。所以够用就行,暂时可以接受。后续在做改进吧。
Over!
最后附上代码
#!/bin/sh if [ -f $.txt ];then
rm $.txt
fi branch=`awk -f split.awk $` for ((i=;i<=$branch;i++));do current=$.$i awk '
BEGIN{
FS="\t"
}
NR=={
for (i=;i<=NF;i++)
fieldLen[i]=
}
{ for (i=;i<=NF;i++)
{
len=length($i)
for (j=;j<=length($i);j++)
if (substr($i,j,) > "\177")
len+=
if (len>fieldLen[i])
{
fieldLen[i]=len
}
} } END{
for (i=;i<=NF;i++)
{
printf "%-s",fieldLen[i]
if (i<NF)
printf "\t"
else
printf "\n" }
}
' $current > $current.schema awk -f execFormat.awk $current.schema $current > $current.txt rm $current
rm $current.schema done for ((i=;i<=$branch;i++));do current=$.$i.txt cat $current >> $.txt rm $current done
format.sh
#!/usr/bin/awk
BEGIN{
FS="\t"
suffix=
filename=ARGV[]
fields=
}
{
if (NF!=fields)
{
fields=NF
suffix+=
}
print $>filename"."suffix
}
END{
print suffix
}
split.awk
#!/usr/bin/awk
BEGIN{
FS="\t"
}
NR==FNR{
for (i=;i<=NF;i++)
fieldLen[i]=$i
}
NR!=FNR{ for (i=;i<=NF;i++)
{
len=
for (j=;j<=length($i);j++)
if (substr($i,j,) > "\177")
len+=
printf "%-'"fieldLen[i]-len"'s",$i if (i<NF)
printf "\t"
else
printf "\n"
}
}
execFormat.awk
使用shell+awk完成Hive查询结果格式化输出的更多相关文章
- 在shell中判断hive查询记录数大小
用途: 根据查询到结果数量来判断,是否需要再执行下个脚本. 1. 查询语句script.q脚本如下: ) as count from test; 2. shell脚本如下: 这里注意hive语句需要 ...
- sql server 查询时间 格式化输出
use test select * from vote insert into vote (contents) values(GETDATE()) insert into vote (contents ...
- hive查询语句入门(hive DDL)
hive DDL 启动hadoop /apps/hadoop/sbin/start-all.sh 开启MySQL库,用于存放hive的元数据 sudo service mysql start 启动hi ...
- shell awk使用(转)
#####awk " 对每一行操作的代码块" 操作的文件 awk -F: '{print $1,$2,$3,$4,$5}' helloworld.sh 打印字段 ...
- shell编程系列15--文本处理三剑客之awk格式化输出printf
shell编程系列15--文本处理三剑客之awk格式化输出printf printf的格式说明符 格式符 含义 %s 打印字符串 %d 打印十进制数 %f 打印一个浮点数 %x 打印十六进制数 %o ...
- Shell 格式化输出printf、awk
目录 Shell 文件的格式化与相关处理 printf.awk 格式化打印printf 案例.格式化输出文件内容 输出命令echo 案例 awk数据处理工具 语法格式 处理流程 AWK内置变量 条件 ...
- shell——awk
awk -F"分隔符" "command" filename awk -F":" '{print $1}' /etc/passwd 字段引用 ...
- shell awk入门
本文参考自 http://www.cnblogs.com/zhuyp1015/archive/2012/07/11/2586985.html awk:好用的数据处理工具 awk 也是一个非常棒的数据处 ...
- awk内置字符串函数 awk 格式化输出
i249 ~ # ps -efl|head -1|awk '$2~/S/{print $2}'Si249 ~ # ps -efl|awk '$2~/S/{print $2}'SSSS printf - ...
随机推荐
- SQL Server编程系列(1):SMO介绍
原文:SQL Server编程系列(1):SMO介绍 续篇:SQL Server编程系列(2):SMO常用对象的有关操作 最近在项目中用到了有关SQL Server管理任务方面的编程实现,有了一些自己 ...
- 一个SQL面试题
面试问题都是基于 bug统计分析续(一)基于SQL的Bug统计方法 中3-2节的讨论抽象出来的. 题目:如果一张表有AC两列,怎么找各种C里不同A的数目.并列出相相应的C的值,并找出每种C下A最多的A ...
- linux_后台启动多个java -jar 程序,及关闭
启动脚本 startup.sh #!/bin/bash x=$(($)) )) do java -jar /home/chenpenghui/crawler/crawler-hb/StartUpIp. ...
- CSS hack 如何区分所有IE浏览器和非IE浏览器
网上方法很多,例如,测试后得出以下结论,多余的话不说了,直入主题: 1.所有的推理IE浏览器 正解:此写法仅仅被lE浏览器识别,非IE浏览器不识别. <!--[if IE]> <st ...
- 【百度地图API】北京周边7日游——图标按路线轨迹行动
原文:[百度地图API]北京周边7日游--图标按路线轨迹行动 任务描述: 春节就要来啦~酸奶小妹的妈妈要从遥远的重庆,来到北京过春节呢!酸奶小妹忙着给妈妈计划,北京周边的旅游线路.计划好路线后,就开始 ...
- MVC+MQ+WinServices+Lucene.Net
MVC+MQ+WinServices+Lucene.Net Demo 前言: 我之前没有接触过Lucene.Net相关的知识,最近在园子里看到很多大神在分享这块的内容,深受启发.秉着“实践出真知”的精 ...
- android KK版本号,如何更改蓝牙设备类型
mediatek/external/bluetooth/bt_cust/bt_cust_table.h { .name = "ClassOfDevice", ...
- Windows环境搭建与第一个C# Sample
Redis入门 - Windows环境搭建与第一个C# Sample 什么是Redis? Redis是一个开源.支持网络.基于内存.键值对存储数据库,使用ANSI C编写.从2013年5月开始,R ...
- 关于MariaDB和Mysql
最近安装数据库的时候发现执行yum install mysql 命令时安装成了mariadb的数据库,它其实也是mysql的一个分支,完全兼容mysql 安装数据库: yum install mysq ...
- android学习8(ListView高级使用)
ListView在android更开放的,于是继续ListView说明使用. 首先创建一个android项目,项目名为ListViewTest. ListView的简单使用 改动布局文件,改动后代码例 ...