linux-结构化成行成列-小文件循环合并成大文件--方便上传到hadoop

#!/bin/bash
#需要预先配置
################
#每种业务输入,输出目录可能不同,分隔符可能也不是",",可以考虑放在配置文件。
#分隔符个数,文件小门限,颗粒度和超时时间已经放在“Tran_configue.ini”。
#输出、输出目录要在一个磁盘分区下,否则大文件mv会导致IO增加很多
################
Input_path="/opt/small_file"
Output_path="/opt/big_file"
CURRENT_PATH=`dirname $0`
cd $CURRENT_PATH
CURRENT_PATH=`pwd`
#echo $CURRENT_PATH
#创建临时文件夹用
dir_time=`date +%Y%m%d.%H%M%S`
if [ ! -d $dir_time ]
then
mkdir $dir_time
fi
#输入目录
if [ ! -d $Input_path ]
then
mkdir $Input_path
fi

#输出目录
if [ ! -d $Output_path ]
then
mkdir $Output_path
fi

#存放错误文件
if [ ! -d $CURRENT_PATH/Error ]
then
mkdir $CURRENT_PATH/Error
fi
#获取时间
today_time=`date +%Y%m%d`
#日志文件
log_file="Unite_${today_time}.log"
#用于标示处理批次时间,不重复
today_seconds=`date +%s`
#########################################
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][=================================Unite starts ]" >>./$log_file
cat Tran_configue.ini | while read LINE
do
#echo 业务类型关键字
prefix=`echo $LINE | awk '{printf $1}'`
#分隔符个数是否正确
FS_NUM=`echo $LINE | awk '{printf $2}'`
#文件大小
F_size=`echo $LINE | awk '{printf $3}'`
#颗粒
period_interval=`echo $LINE | awk '{printf $4}'`
#文件时间戳超时(时间戳距离当前处理时间超过制定时间后,cat后mv到输出目录)
time_out=`echo $LINE | awk '{printf $5}'`
##################################################################################################################
### 过滤文件类型,循环处理,后面 "file_name" 从此处获取。
#每半个小时的文件合并,有可能文件有延迟,前1-2小时的文件需要根据他本来归属的颗粒周期进行处理。
#取一个文件,根据文件名的时间戳进行判断它属于哪个周期,同一个周期的文件合并。
#根据文件名提取时间,如:"web_20151209.0800.dat"
#最右边一个"_",取右边,最右边"."取左边。
#file_name="text_20151209.0800.dat"
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] The number of file is [`ls $Input_path/${prefix}*.dat | wc -l`] " >>./$log_file
for file_name in `ls $Input_path/${prefix}*.dat`
do
#########################################################
#从左边数,最后一个"_",取右边,"20151209.0800.dat"
date_time=${file_name##*_}
#从右边数,最后一个".",取左边,"20151209"
date_time1=${date_time%%.*}
#从左边数,第一个".",取右边,"0800.dat"
date_time2=${date_time#*.}
#从右边数,最后一个".",取左边,"0800"
date_time3=${date_time2%%.*}
#计算文件名对应的秒数“1449619200”
day_senconds=`date +%s -d "${date_time1} ${date_time3}"`
#注意乘号转义符\*,达到去掉周期内余数(为达到后面可能有同一个周期延迟的文件,带上当前处理时间戳区分不同处理批次,二次合并的可能)
#但是需要考虑时间,如果一个周期的文件本来就很小,后续没有新文件,大小不满足“大文件”,需要根据时间戳,判断超时,直接挪动到输出目录。
#1800是半小时,900是15分钟
#echo "period_interval : ${period_interval}"
hour_num=`expr ${day_senconds} / ${period_interval} \* ${period_interval}`
#echo "hour_num : $hour_num"
#根据秒数,取回正常时间,作为文件名。
period_time=`date +"%Y%m%d.%H%M" -d @${hour_num}`
#echo "period_time : $period_time"
#"web_20150102.0800.dat" 合并到 "web_20151209.0800.1449646789.dat"
new_file_name="${prefix}_${period_time}.${today_seconds}.dat"
#echo "new_file_name : $new_file_name"
#########################################################
#取得文件最大的列数,如果列数和定义的列数不一致,文件挪到Error文件夹
column_num=`awk 'BEGIN {FS=",";min_nf=10000;max_nf=0;final_nf=0}{if (NF > max_nf) {max_nf = NF}}{if (NF < min_nf) {min_nf = NF}} END{if (min_nf == max_nf){printf min_nf}else{printf 0}}' $file_name`
#column_num=`awk 'BEGIN {FS=",";count=0} {if (NF >= count) {count = NF}} END{printf count}' $file_name`
#echo $column_num
#如果列数和定义的列数不一致,文件挪到Error文件夹
if [ ${FS_NUM} -ne ${column_num} ];
then
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] Column number of file: ${file_name} is not the same as ${FS_NUM}" >>./$log_file
#文件挪动到Error文件夹
mv $file_name $CURRENT_PATH/Error/
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] File: $file_name has been moved to ${CURRENT_PATH}/Error" >>./$log_file
continue
fi
########################################################
#文件的时间戳是否已经距离当前处理时间比较长?
#如果首次启动很多小文件都是超时间的,文件可能很大
if [ `expr ${today_seconds} - ${day_senconds}` -gt ${time_out} ];
then
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] File: $file_name is older than the time_out threadhold: ${time_out} seconds from now " >>./$log_file
#cat过去可能被别的加载程序取走,第二个文件追加失败,所以改成临时后缀,最后再统一修改。
cat $file_name >> ${Output_path}/${new_file_name}.tmp
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] cat $file_name >> ${Output_path}/${new_file_name}.tmp " >>./$log_file
rm -fr $file_name
continue
fi
#########################################################
#取得文件的大小,大于1G的文件不合并,直接挪动回原来文件夹
file_size=`du -k $file_name | awk '{printf $1}'`
#echo "file size is : $file_size"
#根据实际需要修改,测试用50k
if [ $file_size -gt $F_size ];
then
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] Size of file: ${file_name} is: ${file_size} K,which is greater than ${F_size} K" >>./$log_file
#######################################################
#直接挪动到输出目录(文件名加处理时间戳)
#从左边数,最后一个"/",取右边,去掉目录
#tm_file_name=${file_name##*/}
#从右边数,第一个".",取左边,"web_20151209.0800.123333.dat",去掉.dat
#mv_file_name=${tm_file_name%.*}
#加上处理批次的时间戳(如果之前合并过,文件名中已经带有时间戳,就会出现多个时间戳)
#mv $file_name ${Output_path}/${mv_file_name}.${today_seconds}.dat
#直接挪动,不做任何处理,原始文件就足够大,可能直接挪走,没有时间戳
#mv $file_name ${Output_path}/${tm_file_name}
#cat过去可能被别的加载程序取走,第二个文件追加失败,所以改成临时后缀,最后再统一修改。
cat $file_name >> ${Output_path}/${new_file_name}.tmp
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] cat $file_name >> ${Output_path}/${new_file_name}.tmp " >>./$log_file
rm -fr $file_name
continue
fi
########################################################
#执行合并(没有满足大文件条件,继续放回原来文件夹,可能再次合并)
cat $file_name >> $CURRENT_PATH/$dir_time/$new_file_name
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] cat $file_name >> $CURRENT_PATH/$dir_time/$new_file_name" >>./$log_file
#删除原来文件
rm -fr $file_name
done
########################################################
#原来输出目录的tmp文件,需要改名
if [ `ls ${Output_path}/*.dat.tmp | wc -l` -ne 0 ];
then
rename .dat.tmp .dat ${Output_path}/*.dat.tmp
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] Rename ${Output_path}/*.dat.tmp to ${Output_path}/*.dat " >>./$log_file
fi
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][$prefix] =================================${prefix} finish " >>./$log_file
##################################################################################################################
done
#删除空的文件
find ${CURRENT_PATH}/${dir_time}/ -empty -type f | xargs -i rm -r {}
#合并后的文件挪回到原来目录
if [ `ls ${CURRENT_PATH}/${dir_time}/*.dat | wc -l` -ne 0 ];
then
mv ${CURRENT_PATH}/${dir_time}/*.dat $Input_path/
fi
#目录为空,就删除本次目录
if [ "`ls -A $dir_time`" = "" ];
then
rmdir $dir_time
echo "[ `date +%Y-%m-%d_%T` ][`hostname`]TempDir: $dir_time is deleted " >>./$log_file
else
echo "[ `date +%Y-%m-%d_%T` ][`hostname`]TempDir: $dir_time is not Empty " >>./$log_file
fi
#结束
echo "[ `date +%Y-%m-%d_%T` ][`hostname`][=================================Unite is ended]" >>./$log_file
exit 0

####################################################
vi Tran_configue.ini
web 17 50 1800 1800
stream 17 50 1800 1800
####################################################

1、数据文件所在目录需要设置
vi CatUnite.sh
#需要预先配置
#每种业务输入,输出目录可能不同,分隔符可能也不是",",可以考虑放在配置文件--暂不实现。
#分隔符个数,文件小门限,颗粒度和超时时间已经放在“Tran_configue.ini”。
#输出、输出目录要在一个磁盘分区下,否则大文件mv会导致IO增加很多
Input_path="/opt/small_file"
Output_path="/opt/big_file"
2、文件过滤关键字和列数,文件大小
Tran_configue.ini
关键字<blank>列数<blank>文件大小K单位<blank>颗粒度(如1800半小时一个文件)<blank>超时时间(旧文件本次合并直接到输出目录)
web 17 50 1800 1800
stream 17 50 1800 1800
注意:如果windows编辑文件,注意DOS转换成unix格式。

##################
脚本可以放在crontab -e中定期执行。注意权限。

crontab -e
#每个小时的10,20,40,50分执行
10,20,40,50 * * * * /bin/bash /opt/CatUnite/CatUnite.sh

##################
关于test_data(注意:测试时间原来的文件可能都旧了,条件有先后)
1、#列数有不对的文件,直接挪到/Error文件夹
stream_20151209.1330.dat
web_20151209.1330.dat
2、#时间很久的(超过时间门限),本次合并后直接输出到输出目录
#1330周期内2个合并
stream_20151209.1332.1449663831.dat
stream_20151209.1337.1449663831.dat
web_20151209.1332.1449663831.dat
web_20151209.1338.1449663831.dat
3、当前时间较近的,本次合并后仍然放回原始目录,等跟下次的文件合并成大文件,要么等超时,要么等文件大于大小门限被处理
stream_20151210.1240.1449663831.dat
stream_20151210.1242.1449663831.dat
4、文件超过“小文件门限”,本次合并后,直接输出到输出目录
web_20151210.1310.1449663831.dat
web_20151210.1320.1449663831.dat
stream_20151210.1310.1449663831.dat
stream_20151210.1320.1449663831.dat

####################################
@富兰克林
QQ:455309894
2015-12-10 13:15:00





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值