シェルスクリプトでエラーメッセージを出力するロジックは、前回の「【Shell-Tips】簡単なログ出力ロジックを作ってみました。」で取り扱ってきました。
次は「障害が発生しました。」等のエラーメッセージの取り扱いをどうするか考えてみましょう。 一般的に考えられるのは、下記の2点です。
- スクリプト内に出力するメッセージを予めハードコーディング(べた書き)する。
- メッセージ専用の外部ファイルから取得してログに出力する。
私の経験からいうと、答えは「2.外部ファイルから取得する」一択です。ですが、実際には、ちょっとした規模のプロジェクトでさえも、スクリプト内にハードコーディング(べた書き)しているスクリプトをよく見かけます。
ハードコーディングの問題は、メッセージを書き直す都度、スクリプトに手を入れる必要があることです。
そのスクリプトが多岐にわたって使用されている場合など、ハードコーディングされたメッセージの手直しが元で障害が発生してしまった場合、そのスクリプトを使用するシステム全体の機能が停止してしまうことになりかねません。
この記事では、出力メッセージを外部ファイルから取得する方法について解説していきます。
外部ファイルからメッセージ取得機能を実装する
前提
Beエンジニアでシェルスクリプトを実行する環境は下記の通りとします。
実行環境
BASE_DIR(任意のディレクトリ)
- scripts
- bin(実行スクリプト格納領域)
- func.sh (実行ファイル)
- com(共通スクリプト格納領域)
- comFunc.shrc(共通関数定義ファイル)
- etc(設定ファイル等の格納領域)
- message.conf(メッセージ定義ファイル)
- log(スクリプト実行ログの格納領域)
- スクリプト名.log
- tmp(テンポラリ領域)
- rep(レポート出力領域)
- bin(実行スクリプト格納領域)
外部ファイルは「 <BASE_DIR>/scripts/etc/message.conf 」配下へ作成されるものとして作成します。
メッセージ定義ファイルの作成
メッセージ定義ファイルとして下記のファイルを想定します。
I-00000番台:INFOMATION(情報)
W-00000番台:WARNING(警告)
E-00000番台:ERROR(エラー)
今回は指定された引数の数が正しくなかった場合(引数の値が2つ未満の場合にエラー)を例に作成してみます。
※ 引数が最低3つ以上無いとエラーとします。
[ <BASE_DIR>/scripts/etc/message.conf ]
message.conf
E-00001 user.err " The number of arguments is incorrect. args_count: ["$#"]"
なぜメッセージを英文で記述しているのでしょうか? それは文字エンコーディングの関係にあります。絶対にUTF-8で使用することを前提に作成する場合は、日本語でメッセージを書いても問題ありません。しかし、異なる文字エンコードを使用したOSで実行した場合、文字化けがおきてしまいログとしての使用に耐えません。そのため、特に問題が無いのであればメッセージ文は英語で記述することをお勧めします。
メッセージ取得関数の作成
メッセージ定義ファイルから、引数に指定された番号のメッセージを取得して返します。
1 2 3 4 5 6 7 8 9 10 11 |
# -------------------------------------------------- # Get the message from an external file. # -------------------------------------------------- # param1 messages id # return N/A # -------------------------------------------------- getMsg() { facility=`cat ${MSG_CONF} | grep "${1}" | awk '{print $2}'` line=`cat ${MSG_CONF} | grep "${1}"` echo "${line#*${facility}}" } |
1-7行目:コメント
7行目:関数「getMsg ()」の宣言
8行目:ファシリティー取得(メッセージの不必要な部分のカット用)
9行目:メッセージ定義ファイルから、引数に指定された番号のメッセージを取得
10行目:取得したメッセージを標準出力で返却
共通関数定義ファイルへの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# shell scripts common resource #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # # common.shrc ver.1.0.0 2020.04.24 # # Usage: # -------- # # Description: # 各種機能より呼び出される共通機能を実装する。 # # 設計書 # none # #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # ------------------------------------------------------------ # Constant declaration(定数の宣言領域) # ------------------------------------------------------------ BASE_PATH=${BASE_PATH:-"/root/scripts"} COM_PATH=${BASE_PATH}/com ETC_PATH=${BASE_PATH}/etc LOG_PATH=${BASE_PATH}/log TMP_PATH=${BASE_PATH}/tmp REP_PATH=${BASE_PATH}/rep SCRIPT_NAME=`basename $0` JOB_OK=0 JOB_WR=1 JOB_ER=2 MSG_CONF=${ETC_PATH}/message.conf 👈 メッセージ定義ファイルのパス # ------------------------------------------------------------ # functions (関数を記述する領域) # ------------------------------------------------------------ (省略)前回までの関数が記述されている領域 # -------------------------------------------------- 👈 メッセージ取得関数追記 # Get the message from an external file. # -------------------------------------------------- # param1 messages id # return N/A # -------------------------------------------------- getMsg() { facility=`cat ${MSG_CONF} | grep "${1}" | awk '{print $2}'` line=`cat ${MSG_CONF} | grep "${1}"` echo "${line#*${facility}}" } |
呼び出し側のシェルスクリプト作成
「func.sh(実行シェルスクリプト)」へ実装すると下記の様になります。
※ 「func.sh」は「template.sh」を基に作成しています。
[ template.sh ]は、【Shellの基礎知識】で解説しています。下記のリンクページを参照してください。
[<BASE_DIR>/scripts/bin/func.sh ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
#!/bin/sh #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # # ver.1.0.0 yyyy.mm.dd # # Usage: # # sh /root/scripts/bin/func.sh # # Description: # 例題コマンド実行スクリプト # # 設計書 # なし # #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ . `dirname $0`/../com/comFunc.shrc # ---------------------------------------------------------- # variables (変数の宣言領域) # ---------------------------------------------------------- scope="var" rc=${JOB_ER} # ---------------------------------------------------------- # functions (関数を記述する領域) # ---------------------------------------------------------- scope="func" # -------------------------------------------------- # devider. # -------------------------------------------------- # return N/A # -------------------------------------------------- line (){ echo -e "\\n ------------" echo -e " ▼ ${1}" } # ---------------------------------------------------------- 👈 引数チェック関数追記 # Check the validity of the argument. # ---------------------------------------------------------- # param args # return N/A # ---------------------------------------------------------- checkArgs() { if [ $# -le 2 ]; then logOut $(eval echo $(getMsg E-00001)) exitLog ${JOB_ER} 👈 エラーの場合、強制終了 fi } # ---------------------------------------------------------- # pre-process (事前処理ロジックを記述する領域) # ---------------------------------------------------------- scope="pre" startLog # Check the validity of the argument. checkArgs "$@" 👈 引数チェック関数呼び出し # ---------------------------------------------------------- # main-process (メインロジックを記述する領域) # ---------------------------------------------------------- scope="main" if [ $? -eq ${JOB_OK} ]; then rc=${JOB_OK} fi # ---------------------------------------------------------- # post-process (事後処理ロジックを記述する領域) # ---------------------------------------------------------- scope="post" exitLog $rc |
49行目:引数チェック関数の宣言
50行目:判定文(引数の数が2つ未満の場合はエラー)
51行目:引数が2つ未満の場合はエラーと判定され、メッセージ定義ファイルから、メッセージID「E-00001」のメッセージを取得し、「eval」コマンドを使って2重展開表示しています。
※ 「eval」コマンドを使用してメッセージを2重展開している為、メッセージ中のテキスト「$#」がコマンドとして機能していることに注目してください。
「eval」コマンドは基本組み込みコマンドです。
外部ファイル出力関数の実行
あえてエラーを発生させるために引数を「No1 No2」の2つで実行させます。
# 実行シェルスクリプトからエラーログの出力テスト
$ <BASE_DIR>/scripts/bin/func.sh No1 No2
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@CentOS7 bin]# sh func.sh no1 no2 Output of log file to [ /root/scripts/log/func.log ]. [root@CentOS7 bin]# cd ../log [root@CentOS7 log]# pwd /root/scripts/log [root@CentOS7 log]# ls -l -rw-r--r--. 1 root root 242 4月 25 17:03 func.log [root@CentOS7 log]# cat func.log 2020-04-25 17:03:26 [pre ] - SCRIPT:[ func.sh ] PID:[ 7874 ] STARTED LOG. 2020-04-25 17:03:26 [pre ] - The number of arguments is incorrect. args_count: [2] 2020-04-25 17:03:26 [pre ] - SCRIPT:[ func.sh ] PID:[ 7874 ] ENDED LOG with ERROR. [root@CentOS7 log]# |
「<BASE_DIR>/scripts/bin/func.sh」の引数チェック関数でエラー判定が行われ、その結果エラーとしてログに出力されています。
出力ログの9-11行目「scope領域」が[ pre ]であることに注目してください。引数チェック関数で判定エラーを起こしている為、[ main ] 領域手前(scope領域[ pre ])で処理が終わっていることを示しています。
※ 本スクリプト利用により発生した利用者の損害全てに対し、いかなる責任をも負わないものとし、損害賠償をする一切の義務はないものとします。