现状描述与需求描述

最近梳理系统功能的时候发现现在每个月处理完数据之后,需要给别的系统传送批接口文件,接口文件的内容是来自于Oracle数据表中的数据。我每次都需要手工执行一下存储过程,让数据从正式表中插入到接口表中,然后再借助plsql工具软件sqlplus的spool工具导出接口文件,然后把导出来的7个接口文件,打成zip压缩包,再通过前台系统实现上传(这一部分功能之前已经在前台系统实现部署上线了,详细可参见博文:https://www.cnblogs.com/zhongfengshan/p/9454259.html)。但是现在每个月都需要做这样的事情,很繁琐,而且每个月都需要耗费我的精力,程序猿的个性喜欢探索、创造、和解决问题,本着这样的态度,我开始了这件事的优化之旅。

方案分析

针对于此需求,大概有两种方案。

方案一:把这一系列的操作都写在Java后台的业务逻辑当中,然后通过前台系统每月传参数过去,实现调用。

方案二:可以用shell脚本实现存储过程的调用和数据接口文件的导出和压缩。

由于系统是一个老系统,各种技术框架个分层并没有那么明显,而且有7个文件之多,用Java写起来无论是业务逻辑还是代码量都是及其多,而且不方便测试和调试。相比之下,用shell脚本写,逻辑变得清晰明了,导出的接口文件写在sql文件中,用sqlplus去执行它便可。因此我选用了方案二。

开始实现

第一步:细化业务逻辑,第一步就是需要去调用一个存储过程,存储过程的主要作用是把正式表中当月的数据插入到接口表中。这一步很简单,代码如下

sqlplus zh/dbpassword@zh10g << sql
declare
imonth varchar2(6);
strrtn varchar2(8);
countnum NUMBER;
begin
select to_char(add_months(sysdate,-1),'YYYYMM') into imonth from dual;
SELECT count(*) INTO countnum FROM t_report_if_carrier
WHERE bill_cycle=imonth;
IF (countnum=0) THEN
Dbms_Output.put_line(imonth+':'+countnum);
PR_REPORT_IF(imonth,strrtn);
END IF;
end;
/
sql

计算出当月的上一个月是多少,然后判断表中有没有该月的数据,如果没有,则认为没有执行该存储过程,需要执行存储过程

第二步:需要对导出文件的目录做一下清理,如果上次导出过了,则删除再重新到导出,代码如下:

cd /workforzhongfs/jffile/
rm -rf $report_month
mkdir $report_month
cd $report_month

第三步:把需要执行导出的语句放到一个spoll_file.sql文件中,然后通过sqlplus调用$report_month 代表着需要传给脚本的当前月的上一个月的参数,如现在是2019年04月,则参数为201903

sqlplus zh/dbpassword@zh10g @/workforzhongfs/spoll_file.sql $report_month

spoll_file.sql的内容如下,其中&1代表$report_month传过来的月份参数。

SET NEWPAGE 0
SET SPACE 0
SET LINESIZE 2500
SET PAGESIZE 0
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
SET MARKUP HTML OFF SPOOL OFF
SET COLSEP ' '
SET TRIMSPOOL ON
SET TERMOUT OFF
COL report_name FORMAT a35
COL report_name NEW_VALUE rpt_name
select 'CMBFYDWAL06002A'||&1||'0000000.000' as report_name from dual;
SPOOL &rpt_name
select bank_warrant_no || CHR(9)|| rec_pay_date || CHR(9)|| bank_name || CHR(9)||
record_flag || CHR(9)|| carrier_name || CHR(9)|| carrier_id || CHR(9)||
descript || CHR(9)|| amount_bill || CHR(9)|| exchange_name2 || CHR(9)||
rate_bill || CHR(9)|| amount || CHR(9)|| exchange_name || CHR(9)|| rate || CHR(9)||
amount_rmb || CHR(9)|| bank_fee || CHR(9)|| remark || CHR(9)|| bill_cycle || CHR(9)||
erp_def_code as data
from t_report_if_recpay a
WHERE 1 = 1
and bill_cycle=&1 order by erp_def_code asc;
SPOOL OFF
select 'CMBFYDWAL06005A'||&1||'0000000.000' as report_name from dual;
SPOOL &rpt_name
select ADVANCE_NO || CHR(9)|| carrier_name || CHR(9)|| WARRANT_NO || CHR(9)||
REC_DATE || CHR(9)|| EXCHANGE_NAME || CHR(9)|| AMOUNT || CHR(9)||
AMOUNT_RMB || CHR(9)|| BALANCE || CHR(9)|| BALANCE_RMB as data
from t_report_if_advance
WHERE 1 = 1
and bill_cycle=&1 order by erp_def_code asc;
SPOOL OFF
select 'CMBFYDWAL06006A'||&1||'0000000.000' as report_name from dual;
SPOOL &rpt_name
select BAIL_NO || CHR(9)|| carrier_name || CHR(9)|| warrant_no || CHR(9)||
REC_DATE || CHR(9)|| EXCHANGE_NAME || CHR(9)|| AMOUNT || CHR(9)||
AMOUNT_RMB || CHR(9)|| BALANCE || CHR(9)|| BALANCE_RMB || CHR(9)||
bill_cycle || CHR(9)|| erp_def_code as data
from t_report_if_bail
WHERE 1 = 1
and bill_cycle=&1 order by erp_def_code asc;
SPOOL OFF
select 'CMBFYDWAL06007A'||&1||'0000000.000' as report_name from dual;
SPOOL &rpt_name
select report_month || CHR(9)|| center || CHR(9)|| period || CHR(9)|| types || CHR(9)||
property || CHR(9)|| carrier_name || CHR(9)|| customer_number || CHR(9)||
currency || CHR(9)|| duration || CHR(9)|| amount || CHR(9)|| basiccurrency || CHR(9)||
reference_no || CHR(9)|| bill_cycle || CHR(9)|| erp_def_code as data
from t_report_if_rp
WHERE 1 = 1
and bill_cycle=&1 order by erp_def_code asc;
SPOOL OFF
select 'CMBFYDWAL01001A'||&1||'0000000.000' as report_name from dual;
SPOOL &rpt_name
select DATA_TYPE|| CHR(9) ||CHINESENAME|| CHR(9) ||
CARRIER_NAME|| CHR(9) ||CARRIER_ID|| CHR(9) ||ACCOUNT_CODE|| CHR(9) ||
ACCOUNT_NAME|| CHR(9) ||CONTACT_PERSON|| CHR(9) ||
TELEPHONE_NO|| CHR(9) ||EMAIL_ADDRESS|| CHR(9) ||
BENEFICIARY_NAME|| CHR(9) ||ACCO_LINKMAN_PHONE|| CHR(9) ||
ACCO_LINKMAN_EMAIL|| CHR(9) ||BUSI_MANA_NAME|| CHR(9) ||
ACCO_MANA_NAME as data
from t_report_if_carrier
WHERE 1 = 1
and bill_cycle=&1 order by erp_def_code asc;
SPOOL OFF
select 'CMBFYDWAL06004A'||&1||'0000000.000' as report_name from dual;
SPOOL &rpt_name
select carrier_name || CHR(9)|| destroybill_no || CHR(9)|| settle_flag || CHR(9)||
service_name || CHR(9)|| load_date || CHR(9)|| jfdate || CHR(9)|| settdate || CHR(9)||
exchange_name || CHR(9)|| settle_amount || CHR(9)|| settle_amount_new || CHR(9)||
bill_cycle || CHR(9)|| erp_def_code as data
From t_report_if_destroys
WHERE 1 = 1
and bill_cycle=&1 order by erp_def_code asc;
SPOOL OFF
select 'CMBFYDWAL06001A'||&1||'0000000.000' as report_name from dual;
SPOOL &rpt_name
select carrier_name || CHR(9)|| carrier_no || CHR(9)|| center_name || CHR(9)||
destroybill_no || CHR(9)|| rec_pay || CHR(9)|| buy_property || CHR(9)||
service_no || CHR(9)|| map_name || CHR(9)|| load_date || CHR(9)|| jfdate || CHR(9)||
settdate || CHR(9)|| end_date || CHR(9)|| exchange_name || CHR(9)||
settle_amount || CHR(9)|| settle_amount_rmb || CHR(9)|| current_amount || CHR(9)||
amount_30 || CHR(9)|| amount_90 || CHR(9)|| amount_180 || CHR(9)||
amount_360 || CHR(9)|| AMOUNT_720 || CHR(9)|| AMOUNT_1080 || CHR(9)||
AMOUNT_1440 || CHR(9)|| AMOUNT_1800 as data
From t_report_if_datadetail
WHERE 1 = 1
and bill_cycle=&1 order by erp_def_code asc;
SPOOL OFF
QUIT

遇到的问题

问题一:原本以为这样问题就可以得到解决了,万万没想到,spool导出的文件换行符出现了问题,Windows下的换行符是“\r\n”,而Linux的则是“\n”,这样导致看起来的文件内容是一样的实则是不一样的,用MD5校验之后发现二者不一致。

问题二:导出的CMBFYDWAL01001A2019030000000.000文件每一行的行末有大量的空格,而在windows下用plsql软件导出来的该接口文件没有这个问题。

问题解决

在网上变换各种搜索关键词和不断地试验测试,最终问题都得到解决

问题二的解决,用sed把行末的空格替换成空,

sed 's/[[:space:]][[:space:]]*$//g' $file>$file-sed

问题一的解决,这样子就可以把换行符从Linux的替换为Windows下的换行符。

awk '{ print $0"\r" }'<$file-sed > $file-fs

写了一个循环,当前目录下的所有文件都可以得到替换。

for file in CMBFY*
do
sed 's/[[:space:]][[:space:]]*$//g' $file>$file-sed
awk '{ print $0"\r" }'<$file-sed > $file-fs
echo $file >> $report_month.log
done

还遇到什么问题

解决了上述的问题,那么还遇到什么问题呢?

我把这个脚本 加到crontab中执行的时候,发现脚本开始需要制定月份参数,这样子不又回到了原点么?因此,我必须要解决这个问题。我在网上搜索,大多数人都告诉我用“date -d”可以计算上月的月份,但是我的程序是部署在AIX中的,AIX没有这些奇奇怪怪的选项,采取了个折中的办法,如下代码

month=`date +%m |sed 's/$/b12a01a02a03a04a05a06a07a08a09a10a11a12/;
s/^\(..\)b.*\(..\)a\1.*/\2/'`
year=`date +%Y`
report_month="$year$month"

这样便能计算出上一个月(虽然我也不太知道原理),以下为测试截图

最后加到crontab中便可以自动执行了

最后

万事大吉,愿世界没有bug。

记一次bash脚本开发的经历的更多相关文章

  1. 记一次bash脚本报错原因

    准备部署上次写的 爬虫的定时任务,发现sh脚本 写为最简单的cd ,也报错 后来网上一波了解,原因竟是 : 额,格式问题,我为了图方便是在window里用notepad++写的,然后我再linux 系 ...

  2. Bash 脚本编程语言中的美学与哲学

    我承认,我再一次地当了标题党.但是不可否认,这一定是一篇精华随笔.在这一篇中,我将探讨 Bash 脚本语言中的美学与哲学. 这不是一篇 Bash 脚本编程的教程,但是却能让人更加深入地了解 Bash ...

  3. Linux应用环境实战10:Bash脚本编程语言中的美学与哲学(转)

    阅读目录 一.一切皆是字符串 二.引用和元字符 三.字符串从哪里来.到哪里去 四.再加上一点点的定义,就可以推导出整个Bash脚本语言的语法了 五.输入输出重定向 六.Bash脚本语言的美学:大道至简 ...

  4. Linux 桌面玩家指南:06. 优雅地使用命令行及 Bash 脚本编程语言中的美学与哲学

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...

  5. 【转】如何调试bash脚本

    本文转自:http://coolshell.cn/articles/1379.html Bash 是Linux操作系统的默认Shell脚本.Shell是用来处理操作系统和用户交互的一个程序.Shell ...

  6. 如何调试bash脚本

    Bash 是Linux操作系统的默认Shell脚本.Shell是用来处理操作系统和用户交互的一个程序.Shell的脚本可以帮助用户自动化地和操作系统进行交互.你也可以理解为一种脚本式的编程.即然有编程 ...

  7. Linux中编写Bash脚本的10个技巧

    Shell 脚本编程 是你在 Linux 下学习或练习编程的最简单的方式.尤其对 系统管理员要处理着自动化任务,且要开发新的简单的实用程序或工具等(这里只是仅举几例)更是必备技能. 本文中,我们将分享 ...

  8. 从此编写 Bash 脚本不再难【转】

    从此编写 Bash 脚本不再难 原创 Linux技术 2017-05-02 14:30 在这篇文章中,我们会介绍如何通过使用 bash-support vim 插件将 Vim 编辑器安装和配置 为一个 ...

  9. Linux系统命令与脚本开发

    系统命令 # cat EFO cat >> file << EOF neirong EOF # 清空 >file 清空文件 [root@Poppy conf]# sed ...

随机推荐

  1. Docker容器的基本了解和命令

    一.docker和虚拟机的对比 特性 容器 虚拟机 启动 秒级 分钟级 硬盘使用 一般为MB 一般为GB 性能 接近原生 弱于 系统支持量 单机支持上千个容器 一般几十个 更高效的利用系统资源 更快速 ...

  2. ESP8266 软件实现 Delta-sigma(ΔΣ)调制器 并通过I2S接口输出编码流

    一.关于Delta-sigma(ΔΣ)调制器 Delta-sigma(ΔΣ)调制器是Delta-sigma转换器的核心部件.如下所示为一个简单的一阶Delta-sigma调制器,该调制器产生一个1bi ...

  3. hibernate NUMBER 精度

    通过Hibernate映射实体时会根据数据库中NUMBER类型的精度,生成相应的POJO类中相对应的主键类型.经过亲测结果如下: NUMBER(1) POJO类中生成的是Boolean publicc ...

  4. linux服务开机自动启动

    zookeeper设置开机自动启动 第一种:直接修改/etc/rc.d/rc.local文件 在/etc/rc.d/rc.local文件中需要输入两行,其中export JAVA_HOME=/usr/ ...

  5. CRM--admin组件

    admin组件使用 1.创建一个Django项目 2.在models里面创建表 class Publish(models.Model): name = models.CharField(max_len ...

  6. ieee文献免费下载办法

    sci-hub是个神奇的存在,但突然有短时间不能用了,搜索很久,找到了:https://www.zhihu.com/question/68333471/answer/276287163 这个网址会实时 ...

  7. crontab学习

    概念 日志路径 /var/log/cron 配置路径 vi /etc/crontab 参考 https://www.cnblogs.com/kenshinobiy/p/7685229.html 问题 ...

  8. python 导出数据到excel 中,一个好用的导出数据到excel模块,XlsxWriter

    最近公司有项目需要导出数据到excel,首先想到了,tablib,xlwt,xlrd,xlwings,win32com[还可以操作word],openpyxl,等模块但是 实际操作中tablib 写入 ...

  9. 使用 python 操作 mongodb 常用的操作

    pymongo 的安装命令 pip install pymongo. import pymongo 数据库及集合查询(创建) 连接数据库 查询数据库中的数据库 查询数据库中的集合 创建数据库和集合只需 ...

  10. 配置Linux客户端使用socks5代理上网

    配置Linux客户端使用socks5代理上网   背景 有访问google或者其他海外网站需求的同学可能大都用过或者听过ss,在Windows.Mac.Android.IOS都有现成可用的客户端来协助 ...