勉強メモ30_Perl実装の設計方法について

##0 はじめに
①PerlとDB（今回はMySQL）を使ってユーザー情報入力ファイルのデータの内容を元にユーザー情報テーブルに存在するデータのみを抽出し、出力ファイルを作成する場合、どんな感じで作成するのか、設計方法や開発に関するメモです。

③使うのは、Linux（CentOS7）、Perl、DB(MySQLを利用)

##1 ファイルやディレクトリの構成

```shell:ファイルやディレクトリ構成
/home
  |-rinchome
       |-common      
       |    |-MySQLLib.pm #MySQLアクセスライブラリー
       |    |-userinfo_common_func.pl #バッチ用共通関数
       |
       |-common_lib
       |    |-create_userinfo_common_lib.pm #ユーザー情報作成共通ライブラリー
       |
       |-create_userinfo
       |    |-create_userinfo.pl #ユーザー情報作成バッチ(メイン処理)
       |    |-env
       |       |-userinfo.txt #ユーザー情報作成 exitコード用メッセージ設定
       |       |-userinfo.env  #ユーザー情報作成設定ファイル
       |       |-localhost_UserInfoDb_rinchome.env #接続DB情報
       |
       |-data
       |  |-create_userinfo #出力ファイル
       |          |- backup #出力ファイルのバックファイル
       |          |    |-userinfo_ab_list_YYYYMMDD.csvYYYYMMDDHHMMSS
       |          |    |-userinfo_cd_list_YYYYMMDD.csvYYYYMMDDHHMMSS
       |          |    |-userinfo_ef_list_YYYYMMDD.csvYYYYMMDDHHMMSS
       |          |
       |          |-userinfo_ab_list_YYYYMMDD.csv
       |          |-userinfo_cd_list_YYYYMMDD.csv
       |          |-userinfo_ef_list_YYYYMMDD.csv
       |          
       |-env
       |  |-create_userinfo
       |          |-dpnd_userinfo.env #ログレベルの設定
       |
       |-log
          |-create_userinfo
                  |-userinfo_YYYYMMDDHHMMSS.log #アプリの実行ログ

```

##2 事前インストール
※プロンプトは、$で記載しています。

```shell
#1  最初にユーザー作る
$ useradd rinchome
$ passwd rinchome

#2 log処理を行うため、以下のインストールが必要　一般ユーザではログイン不可　やり方はわからない
$ yum -y install cpan
$ cpan Log::Log4perl
  [local::lib]を選択し、それ以外は、yesを選択してください

#3 ルートに切り替える
su - root 

#4 「use DBI;」を利用するとエラーになるため、DBIのインストールをする

エラーの例↓↓
Can’t locate DBI.pm in @INC (@INC contains: /home/rinchome/perl5 /home/rincho     common_lib/ /home/rinchome/common/ /root/perl5/lib/perl5/5.16.3/x86_64-linux-ead-multi /root/perl5/lib/perl5/5.16.3 /root/perl5/lib/perl5/x86_64-linux-thr-multi /root/perl5/lib/perl5 /usr/local/lib64/perl5 /usr/local/share/perl5 /u     lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/sh/perl5 .) 

$ yum -y install gcc
$ cpan DBI

#5 Perlで利用するMYSQLをインストールする
$ yum -y install perl-DBI perl-DBD-MySQL
$ yum -y remove mariadb-libs
$ rm -rf /var/lib/mysq/
$ yum -y localinstall http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm
$ yum -y install mysql-community-server

$ systemctl stop mysqld ※MySQLを起動
$ systemctl set-environment MYSQLD_OPTS="--skip-grant-tables"
$ systemctl start mysqld

$ mysql -u root ※MySQLにログインして、パスワードを設定
 UPDATE mysql.user SET authentication_string = PASSWORD('User-Info1') WHERE User = 'root' 
 AND Host = 'localhost';
 FLUSH PRIVILEGES;
 quit;

$ systemctl stop mysqld ※MySQLを起動
$ systemctl unset-environment MYSQLD_OPTS
$ systemctl start mysqld;

$ mysql -u root -p　※MySQLにログイン（パスワードを入力して）
 ALTER USER 'root'@'localhost' IDENTIFIED BY 'User-Info1';
 create database UserInfoDb;
 grant all privileges on UserInfoDb.* to rinchome@localhost identified by 'Rinchome-User1';
 flush privileges;
 exit;
$ mysql -u rinchome -p ※もう一度、MySQLにログインできるか確認

#6 ユーザー情報を作成準備

$ mysql -u rinchome -p
 use UserInfoDb;
 CREATE TABLE userinfo (
   shubetsu VARCHAR(2), 
   userid varchar(6),
   name varchar(100),
   telno int
 );
 ※データ投入（今回は、CDの分を出力ファイルに出力する）
 INSERT INTO userinfo (shubetsu, userid, name, telno) VALUES
   ('CD', 'CD1111', 'rinchome', 9876), 
   ('CD', 'CD1111', 'rinchome2', 5432),
   ('aa', 'aa1111', 'rinchome', 9876), 
   ('bb', 'cc1111', 'rinchome2', 5432),
   ('AA', 'aa1111', 'rinchome', 9876), 
   ('BB', 'cc1111', 'rinchome2', 5432);

#7 カタカナ、英字Check処理を行うため、以下をインストール
$ yum -y install perl-ExtUtils-Embed
$ cpan Unicode::Japanese
$ cpan Jcode

#8 Can't locate Date/Calc.pm in @INC　のエラーが出るため、以下をインストール
$ cpan Date::Calc

#9 実行直前に以下をインストール
$ yum -y install perl-DBD-mysql

#10 事前にフォルダーを作成
$ mkdir -p /home/rinchome/common
$ mkdir -p /home/rinchome/common_lib
$ mkdir -p /home/rinchome/create_userinfo/env
$ mkdir -p /home/rinchome/data/create_userinfo/backup
$ mkdir -p /home/rinchome/env/create_userinfo
$ mkdir -p /home/rinchome/log/create_userinfo

```

#3 作成したソース
/home/rinchome/common/MySQLLib.pm

```perl:MySQLLib.pm
#----------------------------------
# title :MySQLLib.pm
# description:MySQLアクセスライブラリー
# author:rinchome
# history:2021/10/10 新規作成
# 
#----------------------------------
package MySQLLib;

use DBI;
use strict;
use Data::Dumper;

{
    #DBディレクトリー設定
    our $_DB_ENV_DIR = "/home/rinchome/create_userinfo/env/";
    
    #DB接続設定ファイル
    our %_DB_ENV_ARRY = (
        "localhost_UserInfoDb_rinchome" => "localhost_UserInfoDb_rinchome.env",
        "localhost_UserInfoTempDb_rinchome" => "localhost_UserInfoTempDb_rinchome.env",
    );
    
    #コンストラクタ
    sub new {
        my ($class) = @_;
        return bless {}, $class;
    }
    
    #DB接続メソッド
    sub MySQLConnect {
        my ($obj, $db_server, $db_name, $db_user) = @_;
        my ($db_env_file, $db_env) = "";
        my ($hostport, $user, $passwd) = "";
        my ($data_source) = "";
        
        #設定ファイルに設定されているか
        if ($_DB_ENV_ARRY{"$db_server\_$db_name\_$db_user"} eq '' ){
            $obj->{'_ErrorMessage'} = "対象サーバー[$db_server] 対象DB[$db_name]未設定!!";
            return -1;
        }
        #DB接続設定ファイルを開く
        $db_env_file = $_DB_ENV_DIR.$_DB_ENV_ARRY{"$db_server\_$db_name\_$db_user"};
        if( open( ENV, $db_env_file) == 0 ){
            $obj->{'_ErrorMessage'} = "DB接続設定ファイル[$db_env_file] open error!";
            return -2;
        }
        $db_env = <ENV>;
        close(ENV);
        
        $db_env =~ s/\s+$//; #終端の空白文字削除
        ($hostport, $db_name, $user, $passwd) = split(/ /, $db_env);
        $data_source = "DBI:mysql:$db_name:$hostport";
        
        #DB接続
        if ( !($obj->{'mysql-dbh'} = DBI->connect($data_source, $user, $passwd,
            {mysql_enable_utf8 => 1, RaiseError => 0, PrintError =>0} ))){
            $obj->{'_ErrorMessage'} = "connect() error! errstr[$DBI::errstr], db_env[$hostport, $db_name, $user]";
            return -3;
        }
        
        return 0;
    }
    
    sub MySQLFinish {
        my ($obj) = @_;
        
        if($obj->{'mysql-sth'}){
           $obj->{'mysql-sth'}->finish; 
        }
        return 0;
    }
    
    sub MySQLDisconnect {
        my ($obj, $syb_ret) = @_;
        
        if($syb_ret == 0){
          
          $obj->MySQLFinish;
          $obj->{'mysql-dbh'}->disconnect;    
        }
    }
    
    sub MySQLBeginTransaction {
        my ($obj, $syb_ret) = @_;
        
        $obj->{'mysql-dbh'}->begin_work;    
        
        return 0;
    }
    
    sub MySQLCommit {
        my ($obj, $syb_ret) = @_;
        
        $obj->{'mysql-dbh'}->commit;    
        
        return 0;
    }
    
    sub MySQLRollback {
        my ($obj, $syb_ret) = @_;
        
        $obj->{'mysql-dbh'}->rollback;    
        
        return 0;
    }
    
    sub MySQLExec {
        my ($obj, $sql_cmd, $fetch_flg) = @_;
        my ($rowdata, $row) = "";
        my (@result) = ();
        my (@data) = ();
        
        #SQL実行準備
        if(!($obj->{'mysql-sth'} = $obj->{'mysql-dbh'}->prepare($sql_cmd) ) ){
            $obj->{'_ErrorMessage'} = "prepare() error! errstr[$DBI::errstr]";
            return -1;
        }
        #SQL実行
        if(!($obj->{'mysql-sth'}->execute) ){
            $obj->{'_ErrorMessage'} = "execute() error! errstr[$DBI::errstr]";
            return -2;
        }
        # SQL実行結果を取得
        if ($fetch_flg eq '1'){
            #1row、複数カラム
            @result = $obj->{'mysql-sth'}->fetchrow_array;
        } elsif ($fetch_flg eq '2'){
            #複数row、1カラム
            while ($rowdata = $obj->{'mysql-sth'}->fetchrow_array){
                push(@result, $rowdata);
            }
        } elsif ($fetch_flg eq '3'){
            #複数row、複数カラム
            while ($row = $obj->{'mysql-sth'}->fetchrow_arrayref){
                push(@result, [@$row]);
            }
        } elsif ($fetch_flg eq '4'){
            #複数selectの複数row、複数カラム
            do{
                while (@data = $obj->{'mysql-sth'}->fetchrow_array){
                    if($obj->{'mysql-sth'}->{syb_result_type} != 4040){
                        next;
                    }
                    push(@result, [@data]);
                }
                
            }while ($obj->{'mysql-sth'}->{syb_more_results});
        }
        
        $obj->{'mysql-sth'}->finish;
        
        return (0, @result);
        
    }
    
    sub MySQLExecFetch {
        my ($obj, $sql_cmd) = @_;
        my ($rowdata, $row) = "";
        my (@result) = ();
        my (@data) = ();
        
        #SQL実行準備
        if(!($obj->{'mysql-sth'} = $obj->{'mysql-dbh'}->prepare($sql_cmd) ) ){
            $obj->{'_ErrorMessage'} = "prepare() error! errstr[$DBI::errstr]";
            return -1;
        }
        #SQL実行
        if(!($obj->{'mysql-sth'}->execute) ){
            $obj->{'_ErrorMessage'} = "execute() error! errstr[$DBI::errstr]";
            return -2;
        }
        
        return (0, $obj->{'mysql-sth'});
        
    }
    
    #エラーメッセージ取得メソッド
    sub getErrMsg {
        my ($obj) = @_;
        return ($obj->{'_ErrorMessage'});
    }
    
    sub MySQLDo {
        my ($obj, $sql_cmd) = @_;
        my ($row) = "";
        
        #SQL実行
        $row = $obj->{'mysql-dbh'}->do($sql_cmd);
        if (!$row){
            $obj->{'_ErrorMessage'} = "do() error! errstr[$DBI::errstr]";
            return -1;
        }
        
        #更新件数が0件の場合
        if ($row eq "0E0"){
            $row = "0";
        }
        
        return (0, $row);
    }
    
}
```

/home/rinchome/common/userinfo_common_func.pl

```perl:userinfo_common_func.pl
#----------------------------------
# title :userinfo_common_func.pl
# description:バッチ用共通関数
# author:rinchome
# history:2021/10/10 新規作成
# 
#----------------------------------
use lib "/home/rinchome/perl5";
use strict;
use Encode;

sub rtrim {
    my(@params) = @_;
    for(@params){
        s/\s+$//;
    }
    
    return wantarray ? @params : $params[0];
    
}
#--------------------------------
# 設定ファイルを読み込む
#--------------------------------
sub readConfFile {
    my ($conf_file) = @_;
    my ($key, $val);
    my (%conf_data);
    
    #ファイルオープン
    if ( !open(PATH, $conf_file)){
      return(1);
    }
    
    while(<PATH>) {
        if(/^#/) {
            next;
        }
        $_ = rtrim( $_ );
        
        #連想配列に追加
        #複数のタブ＆スペースで分割
        ($key, $val) = split( /[\t\s]+/, $_, 2);
        if( ($key ne "") && ( $val ne "")){
            $conf_data{$key} = $val;
        }
    }
    close(PATH);
    
    return (0, %conf_data);
    
}
1;
```







/home/rinchome/common_lib/create_userinfo_common_lib.pm

```perl:create_userinfo_common_lib.pm
#----------------------------------
# title :create_userinfo_common_lib.pm
# description:ユーザー情報作成共通ライブラリー
# author:rinchome
# history:2021/10/10 新規作成
# 
#----------------------------------
package create_userinfo_common_lib;

use strict;
use Data::Dumper;
use POSIX;
use Log::Log4perl qw(get_logger :nowarn);
use Date::Calc qw(:all);

require '/home/rinchome/common/userinfo_common_func.pl';

{
  #各ディレクトリー
  our $BASE_DIR ='/home/rinchome';
  our $DPND_ENV_DIR = '/home/rinchome/env';
  our $LOG_DIR = '/home/rinchome/log';
  
  #オーバーライドさせる
  our $ENV_FILE;
  our $DPND_ENV_FILE;
  my $FUNC_NAME;
  our $EXIT_MSG_FILE;
  
  my %_SELF = ();
  #--------------------------------
  # コンストラクタ
  #--------------------------------
  sub new {
    my($class) = @_;
    return bless {%_SELF}, $class;
  }
  #--------------------------------
  # 終了処理
  #--------------------------------
  sub batchEnd {
      my ($obj, $exit_cd) = @_;
      my ($msg, $result_msg);
      
      $obj->{'mysql'}->MySQLDisconnect(0) if($obj->{'mysql'}->{'dbh'});
      
      if ($exit_cd eq '0') {
          $result_msg = '';
      } elsif ($exit_cd eq '1') {
          $result_msg = 'WARNING';
      } else {
          $result_msg = 'ERROR';
      }
      $msg = sprintf("$obj->{'END_MSG'}", $result_msg);
      
      print($msg);
      $obj->{'log'}->info($msg) if( $obj->{'log'});
      
      $msg = sprintf("処理終了 実行結果コード:[%s] %s", $exit_cd, $obj->getExitMsg($exit_cd));
      
      print($msg."\n");
      if($exit_cd eq '0'){
          $obj->{'log'}->info($msg) if( $obj->{'log'});
      }else{
          $obj->{'log'}->error($msg) if( $obj->{'log'});
      }
      exit($exit_cd);
      
  }
  #--------------------------------
  # 設定ファイルを読み込む
  #--------------------------------
  sub readEnvFile {
      my ($obj, $env_file) = @_;
      my ($ret, %conf_ary, $msg);
      
      #設定ファイルの読み込み
      ($ret, %conf_ary) = readConfFile($env_file);
      if ($ret != 0){
          $msg = "error";
          print($msg);
      }
      
      return \%conf_ary;
  }
  #--------------------------------
  # 終了メッセージ取得
  #--------------------------------
  sub getExitMsg {
      my ($obj, $exit_cd) = @_;
      my ($ret, %tmp_ary, $msg);
      
      #設定ファイルの読み込み
      ($ret, %tmp_ary) = readConfFile($EXIT_MSG_FILE);
      if ($ret != 0){
          $msg = "ret[$ret]=readConfFile($EXIT_MSG_FILE) error!\n";
          print($msg);
          $obj->{'log'}->error($msg) if ($obj->{'log'});
          return "終了メッセージファイルOPEN失敗";
      }
      
      if(exists $tmp_ary{$exit_cd}){
          return $tmp_ary{$exit_cd};
      }else{
          return "終了メッセージ未定義";
      }
      
  }
  
  #--------------------------------
  # ログを初期化
  #--------------------------------
  sub logInit{
      my ($obj) = @_;
      my ($log_file, $log_level, $msg);
      
      $log_file = $obj->{'LOG_FILE'};
      $log_level = $obj->{'LOG_LEVEL'};
      # ログファイルオープンチェック
      if ($log_file eq '' || !open(FH, ">>$log_file")){
          print("ログファイル[$log_file]オープン失敗!\n");
          #$obj->batchEnd('30');
      }
      close(FH);
      
      my $logconf = qq(
          log4perl.appender.file = Log::Log4perl::Appender::File
          log4perl.appender.file.filename  = $log_file
          log4perl.appender.file.mode = append
          log4perl.appender.file.layout = PatternLayout
          log4perl.appender.file.layout.ConversionPattern = %d %5p %F(%M:%L) - %m%n
          log4perl.logger = $log_level, file
      );
      
      Log::Log4perl->init(\$logconf);
      $obj->{'log'} = get_logger();
  }
  #--------------------------------
  # 配列をCSV形式の1行に結合
  #--------------------------------
  sub joinForCsv {
      my ($obj, @csv_arr) = @_;
      my ($line);
      
      $line = join(',', map{"$_"} @csv_arr)."\r\n";
      
      return $line;
  }
  
  #--------------------------------
  # 指定の日から年月加算または減算処理
  #--------------------------------
  sub calcAddYm {
    my ($obj, $baseymd, $cal_y, $cal_m) = @_;
    my ($yy, $mm, $dd);
    
    $yy = substr($baseymd,0,4);
    $mm = substr($baseymd,4,2);
    $dd = substr($baseymd,6,2);
    my ($d_year, $d_month, $d_day) = Add_Delta_YM($yy, $mm, $dd, $cal_y, $cal_m);
    
    return sprintf("%04d%02d%02d", $d_year, $d_month, $d_day);
    
  }
  #--------------------------------
  # 指定の日から日付加算または減算処理
  #--------------------------------
  sub calcAddDay {
    my ($obj, $baseymd, $cal_d) = @_;
    my ($yy, $mm, $dd);
    
    $yy = substr($baseymd,0,4);
    $mm = substr($baseymd,4,2);
    $dd = substr($baseymd,6,2);
    my ($d_year, $d_month, $d_day) = Add_Delta_Days($yy, $mm, $dd, $cal_d);
    
    return sprintf("%04d%02d%02d", $d_year, $d_month, $d_day);
  }
  #--------------------------------
  # 指定の日から年月加算または減算処理
  #--------------------------------
  sub calcAddYmd {
    my ($obj, $baseymd, $cal_y, $cal_m, $cal_d) = @_;
    
    my $addym_ymd = $obj->calcAddYm($baseymd, $cal_y, $cal_m);
    
    return $obj->calcAddDay($addym_ymd, $cal_d);
    
  }
  
}
1;
```

/home/rinchome/create_userinfo/create_userinfo.pl

```perl:create_userinfo.pl
#----------------------------------
# title :create_userinfo.pl
# description:ユーザー情報作成バッチ
# author:rinchome
# history:2021/10/10 新規作成
# 
#----------------------------------
package create_userinfo;

use strict;
use Encode;
use File::Copy 'copy';
use lib '/home/rinchome/common/';
use lib '/home/rinchome/common_lib/';
use create_userinfo_common_lib;
use MySQLLib;
use POSIX;
use Unicode::Japanese;
use Jcode;
my $cp932 = Encode::find_encoding('cp932');

@create_userinfo::ISA = ('create_userinfo_common_lib');
#----------------------------------
#  メインバッチ実行
#----------------------------------
create_userinfo->new->batchMain;
{
    #----------------------------------
    # メイン処理
    #----------------------------------
    sub batchMain {
        my ($obj) = @_;
        #初期処理
        $obj->batchInit();
        
        #ユーザー情報ファイル出力処理
        $obj->batchGetUserInfoOutput();
        
        #終了処理
        $obj->batchEnd('0');
    }
    sub batchInit {
        my ($obj) = @_;
        my ($ret, $msg);
        my (%tmp1, %tmp2, %tmp3);
        my $delete_file;
        my @delete_files;
        
        #バッチ機能名
        $obj->{'FUNC_NAME'} = 'create_userinfo';
        
        #設定ファイル
        $create_userinfo_common_lib::ENV_FILE = "$create_userinfo_common_lib::BASE_DIR/$obj->{'FUNC_NAME'}/env/userinfo.env";
        $create_userinfo_common_lib::DPND_ENV_FILE = "$create_userinfo_common_lib::DPND_ENV_DIR/$obj->{'FUNC_NAME'}/dpnd_userinfo.env";
        $create_userinfo_common_lib::EXIT_MSG_FILE = "$create_userinfo_common_lib::BASE_DIR/$obj->{'FUNC_NAME'}/env/userinfo.txt";
        
        #設定ファイル読み込み
        %tmp1 = %{$obj->readEnvFile($create_userinfo_common_lib::ENV_FILE)};
        %tmp2 = %{$obj->readEnvFile($create_userinfo_common_lib::DPND_ENV_FILE)};
        %tmp3 = (%tmp1, %tmp2);
        $obj->{'conf_ary'} = \%tmp3;
        
        #バッチ実行日時
        $obj->{'start_ym'} = strftime("%Y%m", localtime);
        $obj->{'start_ymd'} = strftime("%Y%m%d", localtime);
        $obj->{'start_ymdhms'} = strftime("%Y%m%d%H%M%S", localtime);
        
        #ログレベルとログファイルの設定
        $obj->{'LOG_LEVEL'} = $obj->{'conf_ary'}->{'LOG_LEVEL'};
        $obj->{'LOG_FILE'} = $obj->{'conf_ary'}->{'LOG_DIR'}. sprintf($obj->{'conf_ary'}->{'LOG_NAME'}, $obj->{'start_ymdhms'});
        
        #処理終了メッセージのフォーマット
        $obj->{'END_MSG'} = "ユーザー情報作成バッチ %sEND\n";
        
        # ログ初期化
        $obj->logInit();
        
        # 実行スクリプト＆ログファイル名出力
        $msg = "[実行スクリプト]$0\n";
        $obj->{'log'}->info($msg);
        
        $msg = "[ログファイル]$obj->{'LOG_FILE'}\n";
        $obj->{'log'}->info($msg);
        
        $msg = "[ログレベル]$obj->{'LOG_LEVEL'}\n";
        $obj->{'log'}->info($msg);
        
        #処理開始ログ出力
        $msg = "ユーザー情報作成バッチ START\n";
        print $msg;
        $obj->{'log'}->info($msg);
        
        $obj->{'mysql'} = new MySQLLib();
        $ret = $obj->{'mysql'}->MySQLConnect($obj->{'conf_ary'}->{'DB_SERVER'}, $obj->{'conf_ary'}->{'DB_NAME'}, $obj->{'conf_ary'}->{'DB_USER'});
        if ($ret != 0){
            #DB接続エラー
            $msg = sprintf("MySQLConnect() error! ret[%d], msg[%s]\n", $ret, $obj->{'mysql'}->getErrMsg);
            print($msg);
            $obj->{'log'}->error($msg);
            #バッチ終了
            $obj->batchEnd('100');
        }
        
        #ユーザー情報出力ファイルの削除(バッチ実行年月日分)
        $obj->{'log'}->info('ユーザー情報出力ファイル(バッチ実行年月日分)の削除処理開始');
        
        #ユーザー情報出力ファイル名の生成
        $delete_file = $obj->{'conf_ary'}->{'USERINFO_DIR'}."*_".$obj->{'start_ymd'}.".csv";
        @delete_files = glob($delete_file);
        
        foreach my $file_full_path (@delete_files){
            #ユーザー情報ファイル削除
            if (unlink($file_full_path) == 0){
                #削除失敗
                $msg = sprintf("ユーザー情報出力ファイル削除失敗 [%s]\n", $file_full_path);
                $obj->{'log'}->error($msg);
                $obj->batchEnd('200');
            }
            $obj->{'log'}->debug(sprintf('ユーザー情報出力ファイル削除: %s', $file_full_path));
        }
        $obj->{'log'}->info('ユーザー情報出力ファイル(バッチ実行年月日分)の削除処理終了');
    }
    
    sub batchGetUserInfoOutput {
        my ($obj) = @_;
        my ($key);
        my ($key_ab_cnt, $key_cd_cnt, $key_ef_cnt);
        my (@userInfoKey);
        my ($msg);
        my ($sql, @data);
        my ($ret);
        my ($userInfoOutputFile);
        my ($header);
        my ($shubetsu,$userid,$name,$telno);
        my (@data_list_arr_csv) = ();
        my $result_list_data;
        my ($userInfoBackUpOutputFile);
        my ($backupTime);
        my ($pos);
        my ($nameVal);
        my ($name_rtn_cd);
        
        $obj->{'log'}->info('ユーザー情報出力ファイル作成処理開始');
        
        #日付の実験
        my $hiduleResultYm;
        $hiduleResultYm = substr($obj->calcAddYm($obj->{'start_ymd'},0,-1),0,6);
        print "現在日付の1か月前：$hiduleResultYm\n";
        my $hiduleResultDay;
        $hiduleResultDay = substr($obj->calcAddDay($obj->{'start_ymd'},-10),0,8);
        print "現在日付の10日前：$hiduleResultDay\n";
        my $hiduleResultYmd;
        $hiduleResultYmd = substr($obj->calcAddYmd($obj->{'start_ymd'},1,1,1),0,8);
        print "現在日付の1年1か月1日後：$hiduleResultYmd\n";
        
        
        @userInfoKey = split(/,/, $obj->{'conf_ary'}->{'USERINFO_KIND_KEY'});
        
        $key_ab_cnt = 0;
        $key_cd_cnt = 0;
        $key_ef_cnt = 0;
        foreach my $userInfoKey(@userInfoKey){
            $key = $userInfoKey;
            
            #ユーザー情報出力ファイルをオープンする
            $userInfoOutputFile = $obj->{'conf_ary'}->{'USERINFO_DIR'}.$obj->{'conf_ary'}->{"USERINFO_FILE_$key"};
            $userInfoOutputFile =~ s/yyyymmdd/$obj->{'start_ymd'}/g;
            if(!open(OUT,">", $userInfoOutputFile)){
                #ユーザー情報出力ファイルオープン失敗
                $msg = sprintf("ユーザー情報出力ファイルオープン失敗[%s]\n", $userInfoOutputFile);
                $obj->{'log'}->error($msg);
                $obj->batchEnd('300');
            }
            
            #ユーザー情報ヘッダー出力
            $header = $obj->{'conf_ary'}->{'USERINFO_FILE_HEADER'};
            if(!print OUT $header."\r\n"){
                #ヘッダー出力失敗
                $msg = sprintf("ユーザー情報ヘッダー出力失敗 ファイル[%s]\n", $userInfoOutputFile);
                $obj->{'log'}->error($msg);
                close(OUT);
                $obj->batchEnd('301');
            }
            
            #ユーザー情報取得
            $sql = sprintf("select shubetsu, userid, name, telno from userinfo where shubetsu = '%s'",$key );
            $obj->{'log'}->debug("sql:$sql");
            ($ret, @data) = $obj->{'mysql'}->MySQLExec($sql,3);
            if ($ret != 0){
              #SQLエラー
              $msg = sprintf("MySQLExec() error! ret[%d], msg[%s], sql[%s]\n", $ret, $obj->{'mysql'}->getErrMsg, $sql);
              print($msg);
              $obj->{'log'}->error($msg);
              #バッチ終了
              $obj->batchEnd('400');
            }
            
            
            foreach my $data_recode(@data) {
                
              #名前チェック(カタカナ、・、英数字のみかのチェック)
              $nameVal = @$data_recode[2];
              $name_rtn_cd = $obj->nameCheck($nameVal);
              
              @data_list_arr_csv = (
                  @$data_recode[0], #種別
                  @$data_recode[1], #ユーザーID
                  @$data_recode[2], #名前
                  @$data_recode[3] #電話番号
              );
              $result_list_data = $obj->joinForCsv(@data_list_arr_csv);
              $msg = sprintf("result_list_data[%s]", $result_list_data);
              $obj->{'log'}->debug($msg);
              #ユーザー情報出力ファイルデータ出力
              if(!print OUT $result_list_data){
                #書き込み失敗
                $msg = sprintf("ユーザー情報出力失敗 ファイル[%s] ユーザーID[%s]\n", $userInfoOutputFile, @$data_recode[1]);
                $obj->{'log'}->error($msg);
                close(OUT);
                $obj->batchEnd('500');
              }
              
              if ($key eq 'AB'){
                  $key_ab_cnt++;
              }elsif($key eq 'CD'){
                  $key_cd_cnt++;
              }elsif($key eq 'EF'){
                  $key_ef_cnt++;
              }
            }
            
            close(OUT);
            
            #ユーザー情報出力件数ログ
            if ($key eq 'AB'){
              $msg = sprintf("ユーザー情報出力ファイル名[%s] 出力件数[%s]", $userInfoOutputFile, $key_ab_cnt);
              $obj->{'log'}->debug($msg);
            }elsif($key eq 'CD'){
              $msg = sprintf("ユーザー情報出力ファイル名[%s] 出力件数[%s]", $userInfoOutputFile, $key_cd_cnt);
              $obj->{'log'}->debug($msg);
            }elsif($key eq 'EF'){
              $msg = sprintf("ユーザー情報出力ファイル名[%s] 出力件数[%s]", $userInfoOutputFile, $key_ef_cnt);
              $obj->{'log'}->debug($msg);
            }
            
            #ユーザー情報出力バックアップファイル作成
            $pos = rindex($userInfoOutputFile,'/');
            $backupTime = strftime("%Y%m%d%H%M%S", localtime);
            $userInfoBackUpOutputFile = $obj->{'conf_ary'}->{'USERINFO_BACKUP_DIR'}
                                    .substr($userInfoOutputFile,$pos+1,length($userInfoOutputFile))
                                    .$backupTime;
            $msg = sprintf("backup_list_data[%s] to[%s]", $userInfoOutputFile, $userInfoBackUpOutputFile);
            $obj->{'log'}->debug($msg);
            if (!copy($userInfoOutputFile,$userInfoBackUpOutputFile)){
                #コピー失敗
                $msg = sprintf("ユーザー情報出力バックアップ作成エラー [%s] \n", $userInfoBackUpOutputFile);
                $obj->{'log'}->error($msg);
                $obj->batchEnd('600');
            }
            
        }
        
    }
    #半角を全角に変換、カタカナ、・、英数字のみかのチェック（文字コードSJIS用チェック）
    sub nameCheck {
        
        my ($obj, $target) = @_;
        
        #半角英数字記号を全角英数字記号に変換
        my $han  = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
           $han .= '!"#\$%&\'\(\)=\^~\\\|@`\[\{;\+:\*\]\},<\.>\/\?_\-ﾞﾟ';
        my $zen  = '０１２３４５６７８９';
           $zen .= 'ａｂｃｄｅｆｇｈｉｊｋｌｍｎｏｐｑｒｓｔｕｖｗｘｙｚ';
           $zen .= 'ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ';
           $zen .= '！”＃＄％’（）＝＾～￥｜＠［｛；＋：＊］｝，＜．＞／？＿゛゜';
        $han = $cp932->decode($han);
        $zen = $cp932->decode($zen);
        $target = $cp932->decode($target);
        
        eval "\$target =~ tr/$han/$zen/";
        
        $target = Unicode::Japanese->new($target)->h2zKana->sjis;
        
        my $j = new Jcode($target);
        #カタカナ、・、英数字のみかのチェック
        foreach my $ch ($j->jfold(2)){
            if (! ($ch =~ /(\x81[\x45]|\x83[\x40-\x96]|\x82[\x60-\x79]|\x82[\x81-\x9a]|\x81[\x4a-\x4b])/)){
                # # カタカナ、・、英数字以外
                return 0;
            }
        }
        
        # カタカナ、・、英数字のみ
        return 1;
        
    }
    
}
```

/home/rinchome/create_userinfo/env/localhost_UserInfoDb_rinchome.env

```perl:localhost_UserInfoDb_rinchome.env
localhost:3306 UserInfoDb rinchome Rinchome-User1
```

/home/rinchome/create_userinfo/env/userinfo.env

```perl:userinfo.env
#----------------------------------
# title :userinfo.env
# description:ユーザー情報作成設定ファイル
# author:rinchome
# history:2021/10/10 新規作成
# 
#----------------------------------

#DB接続情報
DB_SERVER		localhost
DB_NAME		UserInfoDb
DB_USER		rinchome

#ログディレクトリー
LOG_DIR		/home/rinchome/log/create_userinfo/
#ログファイル名
LOG_NAME		userinfo_%s.log

#ユーザー情報出力ファイルディレクトリー
USERINFO_DIR		/home/rinchome/data/create_userinfo/
#ユーザー情報出力ファイルバックアップディレクトリー
USERINFO_BACKUP_DIR		/home/rinchome/data/create_userinfo/backup/
#ユーザー情報出力ファイルヘッダー
USERINFO_FILE_HEADER		種別,ユーザーID,名前,電話番号
#ユーザー情報出力ファイル(AB種別)
USERINFO_FILE_AB		userinfo_ab_list_yyyymmdd.csv
#ユーザー情報出力ファイル(CD種別)
USERINFO_FILE_CD		userinfo_cd_list_yyyymmdd.csv
#ユーザー情報出力ファイル(EF種別)
USERINFO_FILE_EF		userinfo_ef_list_yyyymmdd.csv
#ユーザー情報出力種別キー
USERINFO_KIND_KEY		AB,CD,EF
```

/home/rinchome/create_userinfo/env/userinfo.txt

```perl:userinfo.txt
#----------------------------------
# title :userinfo.txt
# description:ユーザー情報作成 exitコード用メッセージ設定
# author:rinchome
# history:2021/10/10 新規作成
# 
#----------------------------------
0		正常終了
100		DB接続失敗
200		ファイルI/Oエラー(ユーザー情報出力ファイル削除失敗)
300		ユーザー情報出力ファイルオープン失敗
301		ユーザー情報ヘッダー出力失敗
400		ユーザー情報SQL取得処理失敗
500		ユーザー情報データ出力失敗
600		ユーザー情報データバックアップ出力失敗
```

/home/rinchome/env/create_userinfo/dpnd_userinfo.env

```perl:dpnd_userinfo.env
#ログレベル
LOG_LEVEL		DEBUG
```

#4 実行結果
実行の仕方

```shell
$ cd /home/rinchome/create_userinfo
$ perl perl create_userinfo.pl
ユーザー情報作成バッチ START
現在日付の1か月前：202109
現在日付の10日前：20210930
現在日付の1年1か月1日後：20221111
ユーザー情報作成バッチ END
処理終了 実行結果コード:[0] 正常終了
```

中身の確認

```shell
$ /home/rinchome/data/create_userinfo
$ ls -l
-rw-rw-r--. 1 rinchome rinchome   43 10月 10 13:06 userinfo_ab_list_20211010.csv
-rw-rw-r--. 1 rinchome rinchome   94 10月 10 13:06 userinfo_cd_list_20211010.csv
-rw-rw-r--. 1 rinchome rinchome   43 10月 10 13:06 userinfo_ef_list_20211010.csv
drwxrwxr-x. 2 rinchome rinchome 4096 10月 10 13:06 backup

$ cat userinfo_ab_list_20211010.csv
種別,ユーザーID,名前,電話番号
$ cat userinfo_cd_list_20211010.csv ※想定通り、CDだけデータが格納されているのを確認
種別,ユーザーID,名前,電話番号
CD,CD1111,rinchome,9876
CD,CD1111,rinchome2,5432
$ cat userinfo_ef_list_20211010.csv
種別,ユーザーID,名前,電話番号

$ cd /home/rinchome/data/create_userinfo/backup/
$ ls -l ※前回実行時のバックアップデータもあることを確認
-rw-rw-r--. 1 rinchome rinchome 43 10月 10 13:06 userinfo_ab_list_20211010.csv20211010130632
-rw-rw-r--. 1 rinchome rinchome 94 10月 10 13:06 userinfo_cd_list_20211010.csv20211010130632
-rw-rw-r--. 1 rinchome rinchome 43 10月 10 13:06 userinfo_ef_list_20211010.csv20211010130632

$ cd /home/rinchome/log/create_userinfo
$ ls -l
-rw-rw-r--. 1 rinchome rinchome 4114 10月 10 13:06 userinfo_20211010130632.log
$ cat userinfo_20211010130632.log
2021/10/10 13:06:32  INFO create_userinfo.pl(create_userinfo::batchInit:80) - [実行スクリプト]create_userinfo.pl
2021/10/10 13:06:32  INFO create_userinfo.pl(create_userinfo::batchInit:83) - [ログファイル]/home/rinchome/log/create_userinfo/userinfo_20211010130632.log
2021/10/10 13:06:32  INFO create_userinfo.pl(create_userinfo::batchInit:86) - [ログレベル]DEBUG
2021/10/10 13:06:32  INFO create_userinfo.pl(create_userinfo::batchInit:91) - ユーザー情報作成バッチ START
2021/10/10 13:06:32  INFO create_userinfo.pl(create_userinfo::batchInit:105) - ユーザー情報出力ファイル(バッチ実行年月日分)の削除処理開始
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchInit:119) - ユーザー情報出力ファイル削除: /home/rinchome/data/create_userinfo/userinfo_ab_list_20211010.csv
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchInit:119) - ユーザー情報出力ファイル削除: /home/rinchome/data/create_userinfo/userinfo_cd_list_20211010.csv
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchInit:119) - ユーザー情報出力ファイル削除: /home/rinchome/data/create_userinfo/userinfo_ef_list_20211010.csv
2021/10/10 13:06:32  INFO create_userinfo.pl(create_userinfo::batchInit:121) - ユーザー情報出力ファイル(バッチ実行年月日分)の削除処理終了
2021/10/10 13:06:32  INFO create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:143) - ユーザー情報出力ファイル作成処理開始
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:187) - sql:select shubetsu, userid, name, telno from userinfo where shubetsu = 'AB'
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:237) - ユーザー情報出力ファイル名[/home/rinchome/data/create_userinfo/userinfo_ab_list_20211010.csv] 出 力件数[0]
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:253) - backup_list_data[/home/rinchome/data/create_userinfo/userinfo_ab_list_20211010.csv] to[/home/rinchome/data/create_userinfo/backup/userinfo_ab_list_20211010.csv20211010130632]
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:187) - sql:select shubetsu, userid, name, telno from userinfo where shubetsu = 'CD'
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:213) - result_list_data[CD,CD1111,rinchome,9876
]
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:213) - result_list_data[CD,CD1111,rinchome2,5432
]
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:240) - ユーザー情報出力ファイル名[/home/rinchome/data/create_userinfo/userinfo_cd_list_20211010.csv] 出 力件数[2]
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:253) - backup_list_data[/home/rinchome/data/create_userinfo/userinfo_cd_list_20211010.csv] to[/home/rinchome/data/create_userinfo/backup/userinfo_cd_list_20211010.csv20211010130632]
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:187) - sql:select shubetsu, userid, name, telno from userinfo where shubetsu = 'EF'
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:243) - ユーザー情報出力ファイル名[/home/rinchome/data/create_userinfo/userinfo_ef_list_20211010.csv] 出 力件数[0]
2021/10/10 13:06:32 DEBUG create_userinfo.pl(create_userinfo::batchGetUserInfoOutput:253) - backup_list_data[/home/rinchome/data/create_userinfo/userinfo_ef_list_20211010.csv] to[/home/rinchome/data/create_userinfo/backup/userinfo_ef_list_20211010.csv20211010130632]
2021/10/10 13:06:32  INFO /home/rinchome/common_lib//create_userinfo_common_lib.pm(create_userinfo_common_lib::batchEnd:57) - ユーザー情報作成バッチ END
2021/10/10 13:06:32  INFO /home/rinchome/common_lib//create_userinfo_common_lib.pm(create_userinfo_common_lib::batchEnd:62) - 処理終了 実行結果コード:[0] 正常終了
```

#5 実行結果（エラーのパターン）
今回は、「localhost_UserInfoDb_rinchome.env」に記載されている接続パスワードを
「Rinchome-User1」から「Rinchome-User」にわざと変更してテストしてみる

実行の仕方

```shell
$ cd /home/rinchome/create_userinfo
$ perl perl create_userinfo.pl
ユーザー情報作成バッチ START
MySQLConnect() error! ret[-3], msg[connect() error! errstr[Access denied for user 'rinchome'@'localhost' (using password: YES)], db_env[localhost:3306, UserInfoDb, rinchome]]
ユーザー情報作成バッチ ERROREND
処理終了 実行結果コード:[100] DB接続失敗
```

中身の確認

```shell
$ cd /home/rinchome/log/create_userinfo
$ ls -l
-rw-rw-r--. 1 rinchome rinchome 1104 10月 10 13:18 userinfo_20211010131845.log

$ cat userinfo_20211010131845.log
2021/10/10 13:18:45  INFO create_userinfo.pl(create_userinfo::batchInit:80) - [実行スクリプト]create_userinfo.pl
2021/10/10 13:18:45  INFO create_userinfo.pl(create_userinfo::batchInit:83) - [ログファイル]/home/rinchome/log/create_userinfo/userinfo_20211010131845.log
2021/10/10 13:18:45  INFO create_userinfo.pl(create_userinfo::batchInit:86) - [ログレベル]DEBUG
2021/10/10 13:18:45  INFO create_userinfo.pl(create_userinfo::batchInit:91) - ユーザー情報作成バッチ START
2021/10/10 13:18:46 ERROR create_userinfo.pl(create_userinfo::batchInit:99) - MySQLConnect() error! ret[-3], msg[connect() error! errstr[Access denied for user 'rinchome'@'localhost' (using password: YES)], db_env[localhost:3306, UserInfoDb, rinchome]]
2021/10/10 13:18:46  INFO /home/rinchome/common_lib//create_userinfo_common_lib.pm(create_userinfo_common_lib::batchEnd:57) - ユーザー情報作成バッチ ERROREND
2021/10/10 13:18:46 ERROR /home/rinchome/common_lib//create_userinfo_common_lib.pm(create_userinfo_common_lib::batchEnd:65) - 処理終了 実行結果コード:[100] DB接続失敗
```

#6 最後に

・MySQLは、全部テストはしていないためあってるか不明
・カタカナ処理もあっているか不明
・エラーテストも全部は実施していないためあってるかは不明