【入門】シェルスクリプトでバッチ処理を実装する_実装編

以下の記事で、LinuxとMySQLの環境構築を行いました。
本記事は構築した環境でシェルスクリプトを書きました。
[【入門】LinuxサーバーにApacheとMySQLをインストールしながら学ぶLinuxコマンド_環境構築](https://qiita.com/st19/private/a8588d281bcdd772752d)

# 記事を執筆する目的
* 簡単なシェルスクリプトを書けるようになる
* バッチ処理の概要を理解し、実装できるようになる

# 前提

### シェルスクリプト
シェルによって実行される一連の処理を記述したスクリプト<br>
今回はbashのシェルスクリプトを作成する。

### バッチ処理　
データを定期的に一括で処理する方式
今回は、夜間にデータベースにテーブルをコピーして、バックアップデータを作成する処理を実装する。


# 実行環境
SSHクライアントソフトウェア：ターミナル(MacOS)
Linuxディストリビューション：CentOS　Stream8.0
DB：MySQL 8.0

# MySQLにデータベースを作成する
[MySQLの公式サイト](https://dev.mysql.com/doc/index-other.html)にデータサンプルがあるので、そちらのデータを参考にデータベースを作成します。

以下のようなデータをデータベースに構築する。

![Untitled Diagram (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491470/e022f185-be12-1102-9385-8af6e3bcdde7.jpeg)




まずはターミナルからMySQLへ接続し、現在あるデータベースを確認します。

```
# MySQLへ接続する。
$ mysql -u root -p

# 現在MySQLにあるデータベースを確認する。
mysql> show databases;

# 実行結果
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| testdb             |
+--------------------+
5 rows in set (0.01 sec)
```

今回は、新規にデータベースを作成する。

```
# employeesという新規データベースを作成する
mysql> CREATE DATABASE employees;

#　実行結果
Query OK, 1 row affected (0.01 sec)

#　データベースが新規に作成されているかを確認する。
mysql> show databases;

#　実行結果
+--------------------+
| Database           |
+--------------------+
| employees          |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| testdb             |
+--------------------+
6 rows in set (0.02 sec)
```

employeesデータベースの中は空っぽなので、テーブルも作成します。テーブルを作成する前に、操作するデータベースを指定してあげます。

```
# employeesデータベースを選択する
mysql> use employees;

# 実行結果　
Database changed
```

テーブルを作成するには、`CREATE TABLE [テーブル名]([カラム名]　[データ型],・・・・);`と記述する。
テーブルを作成した後に、外部キー等の制約をつける場合は、`ALTER TABLE`文を使用します。
MySQLのSQL文は以下を参考にしました。
参考文献：[MySQLの使い方](https://www.dbonline.jp/mysql/)
参考文献：[MySQL8.0リファレンスマニュアル SQLステートメント](https://dev.mysql.com/doc/refman/8.0/ja/sql-statements.html)

```
# テーブルを作成する。
mysql> CREATE TABLE employees(emp_id int(11), emp_first_name varchar(14), emp_last_name varchar(16), department_id char(4), PRIMARY KEY(emp_id));

mysql> CREATE TABLE departments(department_id char(4),department_name varchar(40),PRIMARY KEY(department_id));

# 外部キーを設定する。
mysql> ALTER TABLE employees ADD FOREIGN KEY fk_department_id(department_id) REFERENCES departments(department_id);

# テーブルにレコードを追加する
INSERT INTO `departments` VALUES
('d001','営業1課'),
('d002','営業2課');

INSERT INTO `employees` VALUES
(10001,'Georgi','Facello','d001'),
(10002,'Bezalel','Simmel','d001'),
(10003,'Parto','Bamford','d001'),
(10004,'Chirstian','Koblick','d001'),
(10005,'Kyoichi','Maliniak','d001'),
(10006,'Anneke','Preusig','d002'),
(10007,'Tzvetan','Zielinski','d002'),
(10008,'Saniya','Kalloufi','d002'),
(10009,'Sumant','Peac','d002'),
(10010,'Duangkaew','Piveteau','d002');
```


```
mysql> SELECT * FROM employees;
+--------+----------------+---------------+---------------+
| emp_id | emp_first_name | emp_last_name | department_id |
+--------+----------------+---------------+---------------+
|  10001 | Georgi         | Facello       | d001          |
|  10002 | Bezalel        | Simmel        | d001          |
|  10003 | Parto          | Bamford       | d001          |
|  10004 | Chirstian      | Koblick       | d001          |
|  10005 | Kyoichi        | Maliniak      | d001          |
|  10006 | Anneke         | Preusig       | d002          |
|  10007 | Tzvetan        | Zielinski     | d002          |
|  10008 | Saniya         | Kalloufi      | d002          |
|  10009 | Sumant         | Peac          | d002          |
|  10010 | Duangkaew      | Piveteau      | d002          |
+--------+----------------+---------------+---------------+
10 rows in set (0.00 sec)

mysql> SELECT * FROM departments;
+---------------+-----------------+
| department_id | department_name |
+---------------+-----------------+
| d001          | 営業1課         |
| d002          | 営業2課         |
+---------------+-----------------+
2 rows in set (0.00 sec)
```




これでデータベースの作成は完了です。


# データベースのバックアップを定期的に取得するシェルスクリプトを作成する

まずはデータベースのバックアップを定期的に取得するシェルスクリプトファイルを作成します。

```
vi database_backup.sh
``` 

処理概要は以下の通りです。
1. バックアップの保存先ディレクトリと、データベースの名前をシェル変数にセットする。
2.　`mysqldump`コマンドを使用して、 DBに登録されているデータをdump（バックアップ）する。
3. 3日前までのバックアップファイルを削除する。

使用するコマンド・オプションは以下の通りです。

| コマンド | コマンド説明 | オプション |
|---|---|---|
| mysqldump | mysqlデータをdump(バックアップ)する | `–-single-transaction` : トランザクションを利用してバックアップを取得する。　<br> `--defaults-extra-file`:通常のオプションファイルに加えて、名前付きオプションファイルを読み取る。 |
|find|　ファイルを検索する|`-type f` :ファイルにみを対象とし検索する。 <br> `name`：ワイルドカードを使用して、ファイルの一部のみを指定し検索する。<br> `-mtime`：任意の日数に更新されたファイルを検索する。例えば、3日以内に変更されたファイルを検索する場合は、`-mtime -3`は3日以内に変更されたファイルを検索するオプションになる。逆に3日より前に変更されたファイルを検索する場合は、`-mtime +3`になる|
|xargs|`xargs <実行したいコマンド>`という形式で、標準入力から受け取ったリストを引数として<実行したいコマンド>を実行する。|今回のシェルスクリプトはオプションを使用しない。|
|rm|ファイル・ディレクトリを削除する|ファイルが存在しない場合でも警告を表示しない|



```
// database_backup.sh

#!/bin/bash

# データベースのバックアップファイルの保存先ディレクトリ
BACKUPDIRPATH=/home/user/backup

# データベースの名前をセットする
DBNAME=employees

# データベースのバックアップを取得し、ダンプファイルを指定のディレクトリに配置する
mysqldump --defaults-extra-file=${BACKUPDIRPATH}/my.conf ${DBNAME} > ${BACKUPDIRPATH}/${DBNAME}_$(date '+%Y-%m-%d').sql

# 3日前までのファイルを削除する
find $BACKUPDIRPATH -type f -name '$DBNAME_*.sql' -mtime +3 | xargs rm -f

```

DB接続に必要なユーザー名やパスワード等の機密情報は別ファイルに格納します。

```
// DB接続情報を入れるファイルを作成する
vi my.conf
```

```
//my.conf

# データベース接続情報

[client]
user=xxxx
password=xxxx
host=xxxx
```

作成したファイルのパーミッションは必要最低限に設定する。

```
chmod 400 my.conf
```

シェルスクリプト上にパスワードを書いて、mysqldumpコマンドを実行すると、以下のように警告が出ます。（ただ、実行はされるようです）

```
mysqldump: [Warning] Using a password on the command line interface can be insecure.

// 意味：コマンドラインインターフェース上でパスワードを入力すると、危険
```
参考文献：[パスワードを入れてmysqlコマンドを実行すると「Warning:Using a password on the command line interface can be insecure」が表示される](https://resource-sharing.co.jp/mysql-command-password-warning/)

# 1日に1回、作成したシェルスクリプトを自動実行されるようにする。

Linuxには、指定したコマンドを定期的に実行する仕組みとして cronがあり、`crontab` コマンドを利用します。

```
// crontab -e : -eオプションでスケジュール設定を編集する

crontab -e 

// 毎日、23時にシェルスクリプトを実行する。
00 23 * * * database_backup.sh
```

# 実行結果

/home/user/backup ディレクトリに、employees_yyyy-mm-dd.sqlのダンプファイルが作成されているかを確認する

```
$ pwd
/home/user/backup

$ ls
employees_2022-01-09.sql
```

MySQLにリストア（復元作業）するためのデータベースを作成します。
employees_2022-01-09.sqlでリストア（復元作業）する。

```
// リストアするためのデータベースを作成する。
CREATE DATABASE　backup_employees;

// リストアする
mysql -u root -p　backup_employees　< employees_2022-01-09.sql
```

リストアした結果を確認する。

```
mysql> SELECT * FROM employees;
+--------+----------------+---------------+---------------+
| emp_id | emp_first_name | emp_last_name | department_id |
+--------+----------------+---------------+---------------+
|  10001 | Georgi         | Facello       | d001          |
|  10002 | Bezalel        | Simmel        | d001          |
|  10003 | Parto          | Bamford       | d001          |
|  10004 | Chirstian      | Koblick       | d001          |
|  10005 | Kyoichi        | Maliniak      | d001          |
|  10006 | Anneke         | Preusig       | d002          |
|  10007 | Tzvetan        | Zielinski     | d002          |
|  10008 | Saniya         | Kalloufi      | d002          |
|  10009 | Sumant         | Peac          | d002          |
|  10010 | Duangkaew      | Piveteau      | d002          |
+--------+----------------+---------------+---------------+
10 rows in set (0.00 sec)

mysql> SELECT * FROM departments;
+---------------+-----------------+
| department_id | department_name |
+---------------+-----------------+
| d001          | 営業1課         |
| d002          | 営業2課         |
+---------------+-----------------+
2 rows in set (0.00 sec)
```

元のデータベースと同じデータが復元できたので、終了です。