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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章