使用shell+awk完成Hive查询结果格式化输出

好久不写,一方面是工作原因,有些东西没发直接发,另外的也是习惯给丢了,内因所致。今天是个好日子,走起!

btw,实际上这种格式化输出应该不只限于某一种需求,差不多是通用的。

需求

--基本的:当前Hive查询结果存在数据与表头无法对其的情况,不便于监控人员直接查看,或者导出到excel中,需要提供一个脚本,将查询结果处理下,便于后续的查看或者操作。

--额外的:A、每次查询出来的结果字段数、字段长度不固定;B、每个数据文件中可能包含不只一套查询结果,即存在多个schema。

想法

对于基本需求而言,无非就是将数据文件用格式化输出整理一下,直接想到了awk。

对于补充的情况,A:需要实现一种机制,基于数据文件,动态地确定格式化输出的参数:字段个数,以及每个格式化字符串的长度参数;B:实现对数据文件根据字段数切割成多段,然后对于每段数据套用前面的脚本处理。

做法

基本需求:

1、指定字段分隔符为“\t”

2、将每个字段按照指定长度格式化输出


1 BEGIN{
2 FS="\t"
3 }
4 {
5 printf "%-"len"s\t",$i
6 }

额外需求A:

需要把代码写成“活”的,适应各种不同的数据文件,如前面所说,实际上就是在执行格式化输出之前,将数据文件扫描一遍,用一个数组记录下文件中每个字段的max
length,然后将这个max length作为该文件内格式化输出的额定宽度。

1、初始化一个fieldLen数组

2、扫描整个文件,更新fieldLen数组

3、将fieldLen数组,用于格式化输出


 1 BEGIN{
2 FS="\t"
3 }
4 NR==1{
5 for (i=1;i<=NF;i++)
6 fieldLen[i]=0
7 }
8 {
9
10 for (i=1;i<=NF;i++)
11 {
12 len=length($i)
13
14 if (len>fieldLen[i])
15 {
16 fieldLen[i]=len
17 }
18 }
19
20 }
21
22 END{
23 for (i=1;i<=NF;i++)
24 {
25 printf "%-s",fieldLen[i]
26 if (i<NF)
27 printf "\t"
28 else
29 printf "\n"
30
31 }
32 }

这里要注意的是,fieldLen的初始化要在NR==1的时候,在BEGIN里面,NF为0

额外需求B:

这里需要一些临时变量,标记分割出来的数据块分支:suffix标记不同的分支,fields当前处理数据块的字段数

处理过程根据前面的临时变量,完成数据文件分割。此处有一个局限在于,对于文件内的多个数据分块,只能处理“AAABBBCCC”这样,同一类数据放在一起的,脚本会分成3块;而对于“AABCABBCC”这种的,则会分割成6块。


 1 BEGIN{
2 FS="\t"
3 suffix=0
4 filename=ARGV[1]
5 fields=0
6 }
7 {
8 if (NF!=fields)
9 {
10 fields=NF
11 suffix+=1
12 }
13 print $0>filename"."suffix
14 }
15 END{
16 print suffix
17 }

基本的思路,就如上面所示。

但是,完成上面的部分,可能不到一半的工作量,接下来,说几个比较麻烦的问题:

A、汉字的问题

这个也是对不齐的主要原因。

在putty里面显示的时候,一个汉字占2个字宽,一个ASCII字符占一个字宽。但是,在调用awk内置的length()函数时,一个汉字跟一个ASCII字符长度是一样的。所以为了在putty上看到的内容是对齐的,需要在格式化输出的时候,对fieldLen的值进行修正。

例子如:

如上,计算得到的fieldLen为4,但实际上需要8;但是在printf的时候,为了对齐,从“abs”到“泰国香蕉”printf的len值是不一样的,根据字段情况,动态决定

所以需要修正的有2处:

1、在计算fieldLen的时候,根据汉字情况,将length($i)获取值加上一个变量


 1 for (i=1;i<=NF;i++)
2 {
3 len=length($i)
4 for (j=1;j<=length($i);j++)
5 if (substr($i,j,1) > "\177")
6 len+=1
7 if (len>fieldLen[i])
8 {
9 fieldLen[i]=len
10 }
11 }

2、在printf格式化输出的时候,根据汉字情况,给fieldLen[i]减去一个变量


 1 for (i=1;i<=NF;i++)
2 {
3
4 len=0
5 for (j=1;j<=length($i);j++)
6 if (substr($i,j,1) > "\177")
7 len+=1
8 printf "%-‘"fieldLen[i]-len"‘s",$i
9
10 if (i<NF)
11 printf "\t"
12 else
13 printf "\n"
14 }

原理比较简单了,就是前面提到的,汉字比ASCII字符多占一个位置,所以在获取fiedlLen的时候,要加上汉字多占的部分;在格式化输出的时候,汉字要减去多占的部分。

这里用到了一种awk内识别汉字的方法,参考了网上一个同学的帖子:


1 for (j=1;j<=length($i);j++)
2 if (substr($i,j,1) > "\177")
3 #TODO

原理就是挨个字符进行检测,“\177”是8进制的127,超过127的都算汉字。

B、多文件输入的问题

按照前面的思路,先要扫描一遍,将数据文件的字段信息存下来,然后再引入字段信息和数据文件,做最终的处理。

这里有一个问题是:是否有必要将字段信息保存成单独文件?从awk的原理来看,基本上是一遍扫描,当第一遍扫描完,之后,游标已经到了文件末尾。这样看不太方便在一个awk处理流程中完成对同一个文件的2次扫描。即使有方法,或许也比较复杂,2遍就两遍吧。

awk多文件输入比较简单,但是我们这里的需求是先读取第一个文件的内容,保存到fieldLen数组;然后利用fieldLen数组,处理第二个文件。这里用到的是NR,FNR这两个变量的作用域不同而完成的:NR服务于整个awk处理,FNR服务于某个文件。


 1 NR==FNR{
2 for (i=1;i<=NF;i++)
3 fieldLen[i]=$i
4 }
5 NR!=FNR{
6
7 for (i=1;i<=NF;i++)
8 {
9 #TODO
10 }

C、printf变量做字宽的问题

前面一直说,根据数据文件,动态地确定字段宽度,所以到最后一步,格式化输出的时候,%s在指定宽度的时候,需要用一个变量指定宽度。这是一个awk语言了解是否透彻的问题,花费了不短时间才搞定,直接贴代码吧。


1 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!

最后附上代码


 1 #!/bin/sh
2
3 if [ -f $1.txt ];then
4 rm $1.txt
5 fi
6
7 branch=`awk -f split.awk $1`
8
9 for ((i=1;i<=$branch;i++));do
10
11 current=$1.$i
12
13 awk ‘
14 BEGIN{
15 FS="\t"
16 }
17 NR==1{
18 for (i=1;i<=NF;i++)
19 fieldLen[i]=0
20 }
21 {
22
23 for (i=1;i<=NF;i++)
24 {
25 len=length($i)
26 for (j=1;j<=length($i);j++)
27 if (substr($i,j,1) > "\177")
28 len+=1
29 if (len>fieldLen[i])
30 {
31 fieldLen[i]=len
32 }
33 }
34
35 }
36
37 END{
38 for (i=1;i<=NF;i++)
39 {
40 printf "%-s",fieldLen[i]
41 if (i<NF)
42 printf "\t"
43 else
44 printf "\n"
45
46 }
47 }
48 ‘ $current > $current.schema
49
50
51 awk -f execFormat.awk $current.schema $current > $current.txt
52
53 rm $current
54 rm $current.schema
55
56 done
57
58 for ((i=1;i<=$branch;i++));do
59
60 current=$1.$i.txt
61
62 cat $current >> $1.txt
63
64 rm $current
65
66 done

format.sh


 1 #!/usr/bin/awk
2 BEGIN{
3 FS="\t"
4 suffix=0
5 filename=ARGV[1]
6 fields=0
7 }
8 {
9 if (NF!=fields)
10 {
11 fields=NF
12 suffix+=1
13 }
14 print $0>filename"."suffix
15 }
16 END{
17 print suffix
18 }

split.awk


 1 #!/usr/bin/awk
2 BEGIN{
3 FS="\t"
4 }
5 NR==FNR{
6 for (i=1;i<=NF;i++)
7 fieldLen[i]=$i
8 }
9 NR!=FNR{
10
11 for (i=1;i<=NF;i++)
12 {
13 len=0
14 for (j=1;j<=length($i);j++)

15 if (substr($i,j,1) > "\177")
16 len+=1
17 printf "%-‘"fieldLen[i]-len"‘s",$i
18
19 if (i<NF)
20 printf "\t"
21 else
22 printf "\n"
23 }
24 }

execFormat.awk

使用shell+awk完成Hive查询结果格式化输出,布布扣,bubuko.com

时间: 2024-10-25 15:20:19

使用shell+awk完成Hive查询结果格式化输出的相关文章

Linux shell 脚本 实现查询出进程的名字,cup内存占用率,启动时间在线状态等格式化输出

目的是查询出进程的名字,cup内存占用率,启动时间在线状态等格式化输出 脚本实现的结果:[[email protected] ~]# sh /app/shell/app_status.shProcessName---------GroupName-------Status-----PID----CPU----MEMORY----StarTime---nginx WEB STOPED NULL NULL NULL NULLhttpd WEB STOPED NULL NULL NULL NULLmy

awk字符串函数(printf格式化输出) -- shell

awk有许多强大的字符串函数 gsub(r,s)                              在整个$0中,用s代替r gsub(r,s,t)                           在整个t中,用s代替r #替换字符串 index(s,t)                             返回s中字符串t的第一位置 #未用过 length(s)                               返回s长度 #c语言strlen match(s,r)

shell awk 的一些用法

#1.打印挂载目录的使用量,默认以空格为分割 df -Ph | awk '{ print $5,$6 }' #2.以空格.冒号.\t.分号为分割 awk -F '[ :\t;]' '{print $1}' #3.打印6.txt文件中的第3行至第5行,NR表示打印行,$0表示文本所有域 awk 'NR==3,NR==5 {print}' 6.txt awk 'NR==3,NR==5 {print $0}' 6.txt #打印6.txt文件中的第3行至第5行的第2列与第4列 awk 'NR==3,N

shell printf格式化输出语句

printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 echo 好. 如同 echo 命令,printf 命令也可以输出简单的字符串: $printf "Hello, Shell\n" Hello, Shell $ printf 不像 echo 那样会自动换行,必须显式添加换行符(\n). printf 命令的语法: printf format-s

2.3.1.shell awk 入门

awk:好用的数据处理工具 awk 也是一个非常棒的数据处理工具!sed 常常用于一整个行的处理, awk 则比较倾向于一行当中分成数个『栏位』(或者称为一个域,也就是一列)来处理.因此,awk 相当的适合处理小型的数据数据处理呢!awk 通常运行的模式是这样的: [[email protected] ~]# awk '条件类型1{动作1} 条件类型2{动作2} ...' filename awk 后面接两个单引号并加上大括号 {} 来配置想要对数据进行的处理动作. awk 可以处理后续接的文件

shell awk入门

本文参考自 http://www.cnblogs.com/zhuyp1015/archive/2012/07/11/2586985.html awk:好用的数据处理工具 awk 也是一个非常棒的数据处理工具!sed 常常用于一整个行的处理, awk 则比较倾向于一行当中分成数个『栏位』(或者称为一个域,也就是一列)来处理.因此,awk 相当的适合处理小型的数据数据处理呢!awk 通常运行的模式是这样的: [[email protected] ~]# awk '条件类型1{动作1}  条件类型2{

【Shell脚本学习15】shell printf命令:格式化输出语句

printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 echo 好. 如同 echo 命令,printf 命令也可以输出简单的字符串: $printf "Hello, Shell\n" Hello, Shell $ printf 不像 echo 那样会自动换行,必须显式添加换行符(\n). printf 命令的语法: printf format-s

[转]shell awk sed tr grep 语法汇总

tr 基本语法 -c          # 用字符串1中字符集的补集替换此字符集,要求字符集为ASCII  -d          # 删除字符串1中所有输入字符  -s          # 删除所有重复出现字符序列,只保留第一个:即将重复出现字符串压缩为一个字符串  [a-z]       # a-z内的字符组成的字符串  [A-Z]       # A-Z内的字符组成的字符串  [0-9]       # 数字串  \octal      # 一个三位的八进制数,对应有效的ASCII字符 

linux shell awk 语法

引用:http://blog.csdn.net/weekly123/article/details/1465675 inux shell awk 语法   Awk 是一种非常好的语言,同时有一个非常奇怪的名称.在本系列(共三篇文章)的第一篇文章中,Daniel Robbins 将使您迅速掌握 awk 编程技巧.随着本系列的进展,将讨论更高级的主题,最后将演示一个真正的高级 awk 演示程序.捍卫 awk在本系列文章中,我将使您成为精通 awk 的编码人员.我承认,awk 并没有一个非常好听且又非