シェルには、コマンドのオプシヨンを解析したリチェックしたりするための、getoptsというコマンドが用意されています。
レビュー時にいつも思うのは、この「getOpts」を使用するエンジニアが少ないこと・・
「getOpts」コマンドは、シェルに対して「-」と"アルファベット1文字"でオプションを指定された場合、それを解析するコマンドです。オプションによって挙動を変えたい時にcase文と共に用います。
実際には習うより慣れろが正しいため、下記にサンプルを実装します。
「getOpts」コマンド
「getOptsコマンドを使うと何がうれしいの?」とよく聞かれることがあります。特にうれしいことはありませんが(笑う・・楽です
もし、シェルスクリプトの引数処理で何かの制御をおなう場合、一番初めに考えれらるのが「if」文だと思いますが、「if」文で引数を制御する場合はパラメータの順番に気を遣わなければならなくなります。
仮にバックアップ用のシェルスクリプト「backup.sh」を作成した場合、必要となる要素は「バックアップ対象」と「バックアップ格納先」になると思います。
ハードコーディング(ソースべた書き)でも良いのですが、それだと「backup.sh」が汎用的に使用できないモノになってしまうため、通常はまず引数で情報の受け渡しを考えるはずです。
if文の場合
# バックアップシェル実行形式
$ backup.sh <バックアップ対象> <バックアップ先パス>
# 「backup.sh」内部の引数チェックロジック
#!/bin/sh
checkArgs() {
if [ -f ${1} ]; then
src=<バックアップ対象>
fi
if [ -d ${2} ]: then
dst=<バックアップ先パス>
fi
}
上記がその際に考えられる実行形式になると思います。
この場合、もし第一引数の<バックアップ対象>に値がはいっていなかった場合どうなるでしょう。値が空であるため、第二引数の<バックアップ先>が第一引数として実行されてしまうのです。
# バックアップシェル実行形式
$ backup.sh <バックアップ対象(空)> <バックアップ先パス>
# 「backup.sh」内部の引数チェックロジック
#!/bin/sh
checkArgs() {
if [ -f ${1} ]; then
src=<バックアップ先パス>
fi
if [ -d ${2} ]: then
dst=
fi
}
チェックが甘くて正常終了してしまった場合、あるはずのバックアップが存在しないことになってしまいます。
getOptsの場合
判り易くバックアップ対象を「-s(source)」、バックアップ先パスを「-d(destination)」としておきます。
# バックアップシェル実行形式
$ backup.sh -s <バックアップ対象> -d <バックアップ先パス>
#!/bin/sh
#-s /-d オプションを指定した場合に出すメッセージを変更する
while getopts s:d: opts
do
case $opts in
s ) src=$OPTARG ;;
d) dst=$OPTARG ;;
* ) usage
exitLog ${JOB_WR} ;;
esac
done;
「while」文の隣に「getOpts」コマンドを記述します。getoptsの次に書かなくてはならない引数は、このシェルスクリプトで処理させようとするオプションを並べたものになります。
もしそのオプションが値を必要とするものならば、その直後に「:(コロン)」を付けます。つまり「while getopts s:d: opts」となります。
「:(コロン)」を付けたオプションの値は、$OPTARGと言いう変数に自動的に格納されます。よって「while」文直下の「case」文の処理では、$OPTARGに代入された値をスクリプト内の変数へ代入します。「src=$OPTARG」
上記のロジックの場合、「-s(source)」、バックアップ先パスを「-d(destination)」何方でもないオプション(例えば[ r ]など)が引数と渡された場合、「case」文の「*)」でキャッチされます。
上記の例では「usage」がコールされ、処理が「exitLog」へ引き渡されます。
「Usege」関数
この「Usage」関数は、特にシェルに用意されているモノでありません。「ヒアドキュメント」を使用して、作成したシェルの実行方法や引数などを表示する自作関数です。
記述方法は簡単で、「ヒアドキュメント」を関数かしただけです。
実際に作成してみます。
usage() {
cat <<EOUSAGE
-----------------------------------------------------------------
Usage: $0 [< options >]
Options:
-b backup_target : Specifies the target file or target to backcup.
-m destination_path : Specify the backup storage directory path.
------------------------------------------------------------------
EOUSAGE
}
これだけです。「$0」は実行しているスクリプト名を表示します。つまり自分の名前です。
EOUSAGEは「End Of Usage」の略です。特に制約等はありませんので開始と終了が同じ文字列なら「EOF」でもなんでもかまいません。
このように「ヒアドキュメント」以外に、特にロジックというロジックはありません。(笑
上記「usage」は、「case」文の「*)」でキャッチされ、実行されます。つまり間違えた引数を指定してシェルを実行した場合、エラーと一緒に出力される自作ヘルプメッセージです。
「usage」の実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[root@CentOS7 bin]# sh func.sh -s /src -r /dst [root@CentOS7 bin]# cd ../log [root@CentOS7 log]# cat func.log 2020-05-12 16:42:13 [ pre ] SCRIPT:[ func.sh ] PID:[ 11858 ] STARTED LOG. func.sh: 不正なオプションです -- r ----------------------------------------------------------------- Usage: func.sh [< options >] Options: -b backup_target : Specifies the target file or target to backcup. -m destination_path : Specify the backup storage directory path. ------------------------------------------------------------------ 2020-05-12 16:42:13 [ pre ] SCRIPT:[ func.sh ] PID:[ 11858 ] ENDED LOG with ERROR. |
どうでしょう? それなりに見えませんか?
「オレ(ワタシ)って意外といけるんじゃね?」と思っていただけると嬉しいです。
最後に、上記を実行したシェルスクリプトを載せておきます。
実行シェルスクリプトを作成する
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
#!/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}" } # -------------------------------------------------- # abort shell. # -------------------------------------------------- # return N/A # -------------------------------------------------- abort () { logOut $(getMsg W-00001) rc=${JOB_WR} } # ---------------------------------------------------------- # how to use. # ---------------------------------------------------------- # return N/A # ---------------------------------------------------------- usage() { cat <<EOUSAGE ----------------------------------------------------------------- Usage: $0 [< options >] Options: -b backup_target : Specifies the target file or target to backcup. -m destination_path : Specify the backup storage directory path. ------------------------------------------------------------------ EOUSAGE } # ---------------------------------------------------------- # pre-process (事前処理ロジックを記述する領域) # ---------------------------------------------------------- scope="pre" startLog # Set a trap. trap abort 1 2 3 15 # Check the validity of the argument. #checkArgs "$@" #-s /-d オプションを指定した場合に出すメッセージを変更する while getopts s:d: opts do case $opts in s) src=$OPTARG ;; d) dst=$OPTARG ;; *) usage exitLog ${JOB_WR} ;; esac done; # ---------------------------------------------------------- # main-process (メインロジックを記述する領域) # ---------------------------------------------------------- scope="main" echo "src=${src} dst=${dst}" if [ $? -eq ${JOB_OK} ]; then rc=${JOB_OK} fi # ---------------------------------------------------------- # post-process (事後処理ロジックを記述する領域) # ---------------------------------------------------------- scope="post" exitLog ${rc} |
※ 本スクリプト利用により発生した利用者の損害全てに対し、いかなる責任をも負わないものとし、損害賠償をする一切の義務はないものとします。