記一次bash腳本開發的經歷

現狀描述與需求描述html

最近梳理系統功能的時候發現如今每月處理完數據以後,須要給別的系統傳送批接口文件,接口文件的內容是來自於Oracle數據表中的數據。我每次都須要手工執行一下存儲過程,讓數據從正式表中插入到接口表中,而後再借助plsql工具軟件sqlplus的spool工具導出接口文件,而後把導出來的7個接口文件,打成zip壓縮包,再經過前臺系統實現上傳(這一部分功能以前已經在前臺系統實現部署上線了,詳細可參見博文:https://www.cnblogs.com/zhongfengshan/p/9454259.html)。可是如今每月都須要作這樣的事情,很繁瑣,並且每月都須要耗費個人精力,程序猿的個性喜歡探索、創造、和解決問題,本着這樣的態度,我開始了這件事的優化之旅。sql

方案分析shell

針對於此需求,大概有兩種方案。windows

方案一:把這一系列的操做都寫在Java後臺的業務邏輯當中,而後經過前臺系統每個月傳參數過去,實現調用。框架

方案二:能夠用shell腳本實現存儲過程的調用和數據接口文件的導出和壓縮。工具

因爲系統是一個老系統,各類技術框架個分層並無那麼明顯,並且有7個文件之多,用Java寫起來不管是業務邏輯仍是代碼量都是及其多,並且不方便測試和調試。相比之下,用shell腳本寫,邏輯變得清晰明瞭,導出的接口文件寫在sql文件中,用sqlplus去執行它即可。所以我選用了方案二。測試

開始實現優化

第一步:細化業務邏輯,第一步就是須要去調用一個存儲過程,存儲過程的主要做用是把正式表中當月的數據插入到接口表中。這一步很簡單,代碼以下spa

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。

相關文章
相關標籤/搜索