ETL应用:使用shell实现文件级校验的方法
BI应用中,对接口规范性约束很重要,接口文件提供需要配套提供该文件的校验文件,校验文件格式如下:
需要对文件进行如下方式校验:采用shell方式实现如下:
#! /bin/bash #********************************************** #*** 程序功能: 文件级校验 #*** 输入参数: <数据文件路径> <校验报告路径> <接口编码> <数据日期> #*** 编 写 人: #*** 编写日期: #*** 修 改 人: #*** 修改日期: #********************************************** #导入常用变量 . ./public.sh #校验成功 readonly VERFIRY_SUCC='00' #接口文件名与规则不符,该规则目前没有做 readonly FILE_NAME_ERR='01' #接口数据文件不存在 readonly FIEL_NOT_EXISTS='02' #接口数据文件无法打开 readonly FILE_OPEN_ERR='03' #文件大小不符 readonly FILE_SIZE_ERR='05' #文件记录数不符 readonly FILE_NUM_ERR='06' #文件数据日期不符 readonly FILE_DATE_ERR='07' #数据文件数据日期非法 readonly FILE_DATE_UNLAW='08' #数据文件记录非法结束符(非回车换行),2008-12-05新增 readonly FILE_RS='11' #数据文件大小超过2,000,000,000Bytes readonly FILE_SIZE_UNLAW='12' #校验文件记录格式错误 readonly VERF_LINE_ERR='16' #校验文件记录非法结束符(非回车换行),2008-12-05新增 readonly VERF_RS='95' #校验文件无法打开 readonly VERF_OPEN_ERR='99' #文件分隔符 readonly FILE_SEP='€' #返回值 verf_return=${VERFIRY_SUCC} #*************************************************** #** 函 数 名: if_verify() #** 函数功能: 判断校验文件的格式是否正确 #** 输入参数: 校验文件名 #*************************************************** if_verify() { #字段数量 field_count=0 #校验文件报告文件名 file_verf_name=$(echo "${file_verf}"|awk -F. '{print $1}')_000.verf #判断校验文件是否能打开 if [ ! -r ${inter_path}/${file_verf} ] then printf ${file_verf}${FILE_SEP}`date +%Y%m%d%H%M%S`${FILE_SEP}${VERF_OPEN_ERR} >${report_path}/f_${file_verf_name} #返回值 return 1 else #判断字段数量是否符合要求 awk -F${FILE_SEP} '{print NF}' ${inter_path}/${file_verf} |while read field_count do #判断字段个数是否是5个,如果不是返回错误 if [ ${field_count} -ne 5 ] then printf ${file_verf}${FILE_SEP}`date +%Y%m%d%H%M%S`${FILE_SEP}${VERF_LINE_ERR} >${report_path}/f_${file_verf_name} #返回值 return 1 fi done fi #无违规项,返回0 #return 0 } #*************************************************** #** 函 数 名: date_unlawless() #** 函数功能: 判断时间是否合法 #** 输入参数: 输入待检查字符 #*************************************************** date_unlawless() { #检查字符 date_string=$1 #字符长度 str_length=${#date_string} #判断日期长度是否为6、8或10 if [ ${str_length} -eq 6 -o ${str_length} -eq 8 -o ${str_length} -eq 10 ] then #年 year=$(echo ${date_string}|cut -c1-4 ) #月 month=$(echo ${date_string}|cut -c5-6 ) else #echo 1 return 1 fi cal ${month} ${year} #判断年月是否合法 if [ $? -eq 1 ] then #echo 1 return 1 else #日期格式为YYYYMMDD if [ ${str_length} -ge 8 ] then #日 day=$(echo ${date_string}|cut -c7-8 ) #小时 hour=$(echo ${date_string}|cut -c9-10 ) #当月最后一天的日期 last_day=$(cal ${month} ${year}|sed '/^$/d'|tail -1|awk '{print $NF}' ) #判断文件中的天是否大于实际天数 if [ ${day} -gt ${last_day} ] then #echo 1 return 1 fi #判断文件中的小时是否大于24 注:只有日期包含小时的时候,才校验小时 if [ ${str_length} -eq 10 ] then if [ ${hour} -gt 23 ]; then return 1 fi fi fi fi # echo 0 } #*************************************************** #** 函 数 名: file_verify() #** 函数功能: 数据文件校验内容,根据校验文件中的记录 #** 判断数据文件是否符合规范要求 #** 输入参数: 校验文件 #*************************************************** file_verify() { #校验文件名 file_name=$1 #函数返回值 _ret_fun=0 #读取校验文件中的记录 while read LINE do verf_return=${VERFIRY_SUCC} #得到文件名 data_file=$(echo ${LINE}|awk -F${FILE_SEP} '{print $1}') #得到校验文件名 verf_file=$(echo "${data_file}"|sed 's/dat/verf/') #得到文件大小 data_file_size=$(echo ${LINE}|awk -F${FILE_SEP} '{print $2}') #得到文件中包含的记录数 data_num=$(echo ${LINE}|awk -F${FILE_SEP} '{print $3}') #得到数据日期 data_date=$(echo ${LINE}|awk -F${FILE_SEP} '{print $4}') #得到文件生成的时间 file_time=$(echo ${LINE}|awk -F${FILE_SEP} '{print $5}') #记录格式头 head_str=${data_file}${FILE_SEP}`date +%Y%m%d%H%M%S`${FILE_SEP} #判断接口文件名与规则是否相符 echo "${data_file}" | grep "${DATAFILE_PATTERN}" 1>/dev/null if [ $? -ne 0 ] then verf_return=${FILE_NAME_ERR} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #判断数据文件是否存在 if [ ! -f ${inter_path}/${data_file} ] then verf_return=${FIEL_NOT_EXISTS} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #判断数据文件是否可以打开 if [ ! -r ${inter_path}/${data_file} ] then verf_return=${FILE_OPEN_ERR} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #实际文件大小不符 file_fact_size=$(ls -Ll ${inter_path}/${data_file}|awk '{print $5}') #判断文件大小是否符合 if [ ${file_fact_size} -ne ${data_file_size} ] then verf_return=${FILE_SIZE_ERR} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #实际文件记录数 file_fact_num=$(awk 'END{print NR}' ${inter_path}/${data_file}) #判断文件记录数不符 if [ ${file_fact_num} -ne ${data_num} ] then verf_return=${FILE_NUM_ERR} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #文件数据日期不符 if [ ${data_date} -ne ${file_data_date} ] then verf_return=${FILE_DATE_ERR} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #数据文件数据日期非法 date_unlawless ${data_date} #判断日期是否合法 if [ $? -ne 0 ] then verf_return=${FILE_DATE_UNLAW} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #数据文件大小超过2,000,000,000Bytes if [ ${file_fact_size} -gt 2147483648 ] then verf_return=${FILE_SIZE_UNLAW} printf "${head_str}${verf_return}\r\n" >${report_path}/f_${verf_file} #函数返回值 let "_ret_fun+=1" continue fi #没有检测到错误返回成功信息 printf "${head_str}${verf_return}\r\n" > ${report_path}/f_${verf_file} #echo "${head_str}${verf_return}" > ${report_path}/f_${verf_file} done < ${file_name} #返回值 return ${_ret_fun} } #*************************************************** #** 函 数 名: main() #** 函数功能: 主程序 #** 输入参数: 数据文件路径 校验报告路径 接口编码 数据日期 #*************************************************** #判断参数是否合法 if [ $# -ne 4 ] then echo "parameter error!" echo "Usage: ./file_verrify.sh <数据文件路径> <校验报告路径> <接口编码> <数据日期>" exit 1 fi #初始化变量,文件路径 inter_path=$1 #校验报告文件路径 report_path=$2 #接口编码 inter_code=$3 #数据日期 file_data_date=$4 #数据文件的正则表达式配置 DATAFILE_PATTERN="[a,i]_.*${file_data_date}_.*${inter_code}_[0-9]\{2\}_[0-9]\{3\}.dat" #校验报告路径 if [ ! -d ${report_path} ] then #建目录 mkdir -p ${report_path} fi #提取校验文件 if [ -z `ls ${inter_path}/*${file_data_date}_*${inter_code}_??.verf 2>/dev/null` ]; then echo "verify file is not exist! " exit 1 else file_verf=$(ls ${inter_path}/*${file_data_date}_*${inter_code}_??.verf|awk -F/ '{print $NF}') fi #进行校验文件合法性判断 if_verify #如果校验文件错误,不进行数据文件的校验 if [ $? -ne 0 ]; then echo "verify file error! " exit 1 fi #进行数据文件的校验 file_verify ${inter_path}/${file_verf} if [ $? -ne 0 ]; then echo "data file error! " exit 1 fi
序号 |
信息内容 |
数据类型及长度 |
说明 |
1 |
接口数据文件名称 |
CHAR(50) |
|
2 |
文件的大小(字节数) |
NUMBER(20) |
文件的物理存储大小 |
3 |
文件中包含的记录数 |
NUMBER(20) |
|
4 |
数据日期 |
CHAR(10) |
如果抽取周期为小时,则格式则格式为:YYYYMMDDHH(HH采用24小时制,取值00-23);如果抽取周期为日,则格式则格式为:YYYYMMDD;如果抽取周期为月,则格式为:YYYYMM; |
5 |
文件的生成时间 |
CHAR(14) |
日期格式:YYYYMMDDHH24MISS |
6 |
0x0D0A |
|
行间分隔符-回车换行符 |
序号 |
校验结果代码 |
校验结果描述 |
|
1 |
00 |
校验成功 |
|
2 |
01 |
接口文件名与规则不符 |
|
3 |
02 |
接口数据文件不存在 |
|
4 |
03 |
接口数据文件无法打开 |
|
5 |
05 |
文件大小不符 |
|
6 |
06 |
文件记录数不符 |
|
7 |
07 |
文件数据日期不符 |
|
8 |
08 |
数据文件数据日期非法 |
|
9 |
10 |
数据文件接口单元编码非法 |
|
10 |
11 |
数据文件记录非法结束符(非回车换行) |
|
11 |
12 |
数据文件大小超过2,000,000,000Bytes |
|
12 |
13 |
接口数据文件重复上传 |
|
13 |
14 |
数据文件数据日期与期待日期不符 |
|
14 |
16 |
校验文件记录格式错误 |
|
15 |
92 |
校验文件数据日期与期待日期不符 |
|
16 |
93 |
校验文件重复上传 |
|
17 |
94 |
校验文件接口单元编码非法 |
|
18 |
95 |
校验文件记录非法结束符(非回车换行) |
|
19 |
97 |
校验文件数据日期非法 |
|
20 |
98 |
校验文件记录长度不符 |
|
21 |
99 |
校验文件无法打开 |
学无止境