当SFTP中数据量过大时,一台机器可能抽取不过来,可以利用多台机器抽取,该shell脚本实现的功能有:
- 多台机器比对已下载文件和sftp中文件,下载没有下载过的文件
- 多台机器不需要通信,简化布置难度
- 将下载到的大量小文件合并后,将“前缀-日期....”命名的文件上传到hdfs对应的日期目录下
- 记录下载的文件名、文件大小、文件数量和合并后的文件数量、文件大小
- 需要用到except组件,请单独安装,except组件可离线安装,请字行百度
#!/bin/bash
#/home/data/script/cmp_getfile.sh
source ~/.bash_profile
shopt -s expand_aliases
#
# 比对上传ftp文件
# 当文件在ftp里存在已下载文件中时,从ftp下载文件到hdfs
#
# 参数: ftp路径 本地文件存放地址 hdfs路径 hive表名 命名前缀 机器总数 下载序列
# ./cmp_get_file.sh /data/sftp/N10 /home/data/5G/SA /user/test/n10_hdfs test_5g_n10 N10-Anhui 4 0
#需要配置的参数 script_path
#下载序列为(0到机器数减一),每台机器不可重复
#共有多少台机器下载
# 程序会根据文件名,计算md5值,取前3位数值,除以机器总数,根据余数是否等于下载序列值
# 来确定是否下载该文件
#
#脚本所在目录
script_path='/home/data/script/'
#机器数
jc_nums=$6
#=========================
m_ftpip='sftpname@sftpip'
m_pwd='xxxxxx'
m_ftp_path=$1
m_local_ptah=$2
m_hdfs_path=$3
m_hive_name=$4
m_file_pre=$5
id=$7
#对id做简单校验
if [ ${id:--1} -lt 0 -o ${id:--1} -ge $jc_nums ];then
echo 'parameters error!!'
exit 1
fi
#临时日志文件存放地址
log_path_tmp="${script_path}/tmp_logs"
#下载文件记录存放地址
file_info_path="${script_path}/file_logs"
#上传文件记录存放地市 ##重要 从ftp上记录下载不在该记录的数据
down_list_path="${script_path}/config"
[ ! -d ${log_path_tmp} ] && mkdir -p ${log_path_tmp}
[ ! -d ${file_info_path} ] && mkdir -p ${file_info_path}
[ ! -d ${down_list_path} ] && mkdir -p ${down_list_path}
down_list="${down_list_path}/${m_file_pre}_downlist.list"
time_falg=`date +"%F %T"`
time_falg_s=`date +%s`
#今天和昨天时间
tday=`date +%Y%m%d`
yday=`date +%Y%m%d -d "$tday -1 day"`
echo "[${time_falg}] =================== script is running "
#本次流程需要下载的文件列表
x_time=`date +%Y%m%d%H%M`
re_path=${log_path_tmp}/${x_time}_${m_file_pre}_filelist.list
#本次流程实际下载的文件列表
re_path1=${log_path_tmp}/${x_time}_${m_file_pre}_downlist.list
#ftp 存放文件目录
#函数参数(4个参数): ftp路径 hdfs上传路径 下载文件存放本地地址
# ex: get_file_list /data/sftp/N10 /user/test/n10_hdfs /home/data/5G/SA
function get_file_list(){
if [ $# -ne 4 ];then
echo '[ERROR] get_file_list error!'
return 1
fi
#ftp ip 地址
ftpip=$1
#ftp 路径
ftp_tmp=$2
#hdfs路径
hdfs_path=$3
#下载文件存放路径
file_ptah_tmp=$4/${x_time:-0}
#判断下载路径是否存在
if [ ! -d "${4:=0}" ];then
echo 'ERROR : download path not exists!!'
exit 1
fi
log_path_fd=${log_path_tmp}/${x_time}_${m_file_pre}_ftpfile.list
log_path_hd=${log_path_tmp}/${x_time}_${m_file_pre}_hdfsfile.list
file_list=${file_info_path}/${m_file_pre}_file_list.list
#获取ftp文件列表
#开头不能有空格
#5G数据sftp上传文件成功后会生成一个ok文件,根据ok文件来抽取数据
expect << EOF | sed 's/\x0d//g' | awk '$9 ~ /txt\.ok/{print $9}' | sort | sed 's/txt.ok/txt.gz/g'>${log_path_fd}
set timeout 600
spawn sftp ${m_ftpip}
expect "password:"
send "${m_pwd}\r"
expect "sftp>"
send "cd ${ftp_tmp}/${yday}\r"
expect "sftp>"
send "ls -lt ${m_file_pre}-*ok\r"
send "cd ${ftp_tmp}/${tday}\r"
expect "sftp>"
send "ls -lt ${m_file_pre}-*ok\r"
expect "sftp>"
send "quit\r"
expect eof
EOF
#获取hdfs文件列表 --默认有天级和小时级
#hadoop fs -ls ${hdfs_path}/${yday}/*/ ${hdfs_path}/${tday}/*/ 2>/dev/null | grep '^-.*txt.*' | awk -F ' ' '{split($8,tA,"/");print tA[length(tA)]}' | sort >${log_path_hd}
#获取已下载文件列表
cat ${down_list} ${log_path_tmp}/*_${m_file_pre}_filelist.list 2>/dev/null | sort -u >${log_path_hd}
#获取需要下载文件列表
# re_path
grep -f ${log_path_hd} -vxF ${log_path_fd} \
| awk '{cmd1="echo -n "$1" | md5sum";
filename=$1;
cmd1 | getline;
md5str=substr($1,0,3);
close(cmd1);
x=strtonum("0x"md5str)%'$jc_nums';
if(x=='$id'){print filename}}' >${re_path}
rm -f ${log_path_hd:-0}
rm -f ${log_path_fd:-0}
#拼接下载文件命令
#拼接下载文件
yday_cmd_str=''
for file_name in `cat ${re_path} | grep ".*-.*-${yday}.*-.*-"`;do
yday_cmd_str="${yday_cmd_str}
expect \"sftp>\"
send \"get ${file_name}\\r\""
done
tday_cmd_str=''
for file_name in `cat ${re_path} | grep ".*-.*-${tday}.*-.*-"`;do
tday_cmd_str="${tday_cmd_str}
expect \"sftp>\"
send \"get ${file_name}\\r\""
done
#拼接完整sftp 下载命令
ftp_getfile_cmd="expect << EOF 2>&1 | grep -v '^sftp>' | grep -v '^Fetching'
set timeout 600
spawn sftp ${m_ftpip}
expect \"password:\"
send \"${m_pwd}\\r\"
expect \"sftp>\"
send \"progress\\r\"
"
#昨天未下载文件
if [ "$yday_cmd_str" != '' ];then
ftp_getfile_cmd="$ftp_getfile_cmd
expect \"sftp>\"
send \"cd ${ftp_tmp}/${yday}\\r\"
${yday_cmd_str}
"
fi
#今天未下载文件
if [ "$tday_cmd_str" != '' ];then
ftp_getfile_cmd="$ftp_getfile_cmd
expect \"sftp>\"
send \"cd ${ftp_tmp}/${tday}\\r\"
${tday_cmd_str}"
fi
ftp_getfile_cmd="$ftp_getfile_cmd
expect \"sftp>\"
send \"quit\\r\"
expect eof
EOF"
#备份下载命令
#echo "${ftp_getfile_cmd}" >get_cmd.list
#执行下载命令
gf_time_s=`date +%s`
[ ! -d ${file_ptah_tmp} ] && mkdir -p ${file_ptah_tmp}
cd ${file_ptah_tmp}
eval "${ftp_getfile_cmd}"
#统计下载文件信息
re_path1=${log_path_tmp}/${x_time}_${m_file_pre}_downlist.list
ls -l ${m_file_pre}-*-*-*.txt* 2>/dev/null | awk '$9 ~ /txt\.gz/{print $9"|"$5}' >${re_path1}
re_get_file_nums=`cat ${re_path1} | wc -l`
#应该下载文件总大小
total_size=0
#应该下载文件各时段信息
re_msg='#'
for day_time in `awk -F '-' '{print substr($3,1,8)}' ${re_path1} | sort -u`;do
day_files=0
day_size=0
for name_size in `grep ".*-.*-${day_time}.*-.*-" ${re_path1} `;do
let day_files++
tmp_size=`echo "${name_size}" | cut -d '|' -f 2`
tmp_size=${tmp_size:=0}
let day_size=day_size+tmp_size
done
re_msg="${re_msg}
# [${time_falg}] ${day_time} : ${day_files} ${day_size}"
let total_size=total_size+day_size
done
echo "# [${time_falg}]"' ======== total files : '${re_get_file_nums}' total size : '${total_size}' ========' >>${file_list}
cat <<EOF >>${file_list}
${re_msg}
EOF
gf_time_e=`date +%s`
#输出日志下载时间
echo "[${time_falg}] get file time : "`expr $gf_time_e - $gf_time_s`
echo "[${time_falg}] get file nums : "$re_get_file_nums
echo "[${time_falg}] get file size : "$total_size
}
#上传文件函数
#参数 : 本地文件存放地址 hdfs路径 hive表名(暂时用不到)
function put_file() {
pf_time_s=`date +%s`
tmp_time=`date +"%F %T"`
#本地文件存放地址
tmp_path=${1}/${x_time:-0}
#hdfs路径
put_hdfs_path=$2
#hive表名
hive_name=$3
#判断下载路径是否存在
if [ ! -d "${tmp_path}" ];then
echo 'ERROR : download path not exists!! '$tmp_path
exit 1
fi
cd ${tmp_path}
#压缩后的文件数
file_nums=0
file_size=0
#上传文件 --数据量暂时过小 小时级分区不启用 默认00分区
#小时级分区
#re_msg 日志信息
re_msg='#'
for file_time in `ls ${m_file_pre}-*-*-*.txt* 2>/dev/null | sed 's/.*-.*-\([0-9]\{10\}\)[0-9]\{4\}-.*-.*/\1/' | sort | uniq`;do
tmp_file_size=0
#小时级分区
#for file_time in `ls ${m_file_pre}-*-*-*.txt* 2>/dev/null | sed 's/.*-.*-.*-\([0-9]\{10\}\)[0-9]\{4\}-.*/\1/' | sort | uniq`;do
#文件合并 128M以上 一个文件 134217728
size_128m=134217728
#该时间段的文件名和大小
tmp_file_strs=`ls -l ${m_file_pre}-${file_time}*-*.txt.gz 2>/dev/null | awk '$9 ~ /txt\.gz/{print $9"|"$5}'`
hh_files=0
hh_size=0
for tmp_file_name in $tmp_file_strs;do
tmp_file=`echo ${tmp_file_name} | cut -d '|' -f 1`
file_size2=`echo ${tmp_file_name} | cut -d '|' -f 2`
#时间段数据量
let hh_files=hh_files+1
let hh_size=hh_size+${file_size2:=0}
#总数据量
let file_size=file_size+file_size2
if [ $tmp_file_size -eq 0 ];then
meger_file=$tmp_file
let file_nums=file_nums+1
let tmp_file_size=tmp_file_size+file_size2
continue
fi
if [ $file_size2 -gt $size_128m ];then
let file_nums=file_nums+1
continue
fi
cat ${tmp_file} >>${meger_file:-0}
rm -f ${tmp_file:-0}
if [ $tmp_file_size -gt $size_128m ];then
tmp_file_size=0
fi
done
re_msg="${re_msg}
# [${time_falg}] ${file_time} : files-${hh_files} size-${hh_size}"
file_day=${file_time:0:8}
file_hh=${file_time:8:2}
#判断上传路径是否存在
if ! hadoop fs -test -e ${put_hdfs_path}/${file_day}/${file_hh};then
echo 'ERROR : upload path not exists!! ' ${put_hdfs_path}/${file_day}/${file_hh}
rm -f ${m_file_pre}-${file_time}*-*.txt*
continue
fi
hadoop fs -put -f ${m_file_pre}-${file_time}*-*.txt.gz ${put_hdfs_path}/${file_day}/${file_hh}/
#上传成功将下载文件写入下载列表
if [ $? -eq 0 ];then
grep "${m_file_pre}-${file_time}.*-.*.txt.gz" ${re_path1} >>${file_list}
grep "${m_file_pre}-${file_time}.*-.*.txt.gz" ${re_path1} | awk -F '|' '{print $1}' >>${down_list}
else
echo '[ERROR] put files to hdfs error! '"${m_file_pre}-${file_time}*-*.txt.gz" >>${file_list}
fi
rm -f ${m_file_pre}-${file_time}*-*.txt*
done
#日志信息 文件数
cat <<EOF >>${file_list}
${re_msg}
EOF
#删除已下载文件列表里不属于今天和昨天的文件
sed -i '/'${tday}'\|'${yday}'/!d' ${down_list}
#判断tmp_path参数长度,防止误删
cd -
if [ "${#tmp_path}" -gt 20 ];then
rm -rf ${tmp_path:-0}
fi
pf_time_e=`date +%s`
echo "[${time_falg}] put megerfile nums : ${file_nums}"
echo "[${time_falg}] put mergerfile size : ${file_size}"
echo "[${time_falg}] put file time : "`expr $pf_time_e - $pf_time_s`
}
get_file_list "$m_ftpip" "$m_ftp_path" "$m_hdfs_path" "$m_local_ptah"
time_flag_2=`date +%s`
put_file "$m_local_ptah" "$m_hdfs_path" "$m_hive_name"
rm -f ${re_path:-0}
rm -f ${re_path1:-0}
time_flag_e=`date +%s`
#脚本执行时长
total_times=`expr $time_flag_e - $time_falg_s`
#时长超过一小时则告警
if [ $total_times -gt 3600 ];then
/data/script/5g_sendMsg.sh "$m_file_pre" "$total_times" "${time_falg}"
#/data/script/send_msg.sh '156xxxxxxxx' "XX XX $m_file_pre脚本执行时间超过1小时,请注意!! [000.00.00.000]"
fi
echo "[${time_falg}] script total time : "$total_times
echo "[${time_falg}] get_file_list time : "`expr $time_flag_2 - $time_falg_s`
echo "[${time_falg}] put_file time : "`expr $time_flag_e - $time_flag_2`
echo "[${time_falg}] =================== script is over "