Torihaji's Growth Diary

Little by little, no hurry.

オプション付き自作シェルスクリプトの作り方(sh,bash)

はじめに

みなさん、こんにちは。torihaziです。

本日は タイトル通り オプション付きのシェルスクリプトを作成してみました。

作成したきっかけは本業で近しいものを作ったからです。

業務効率化の目的です。

それでは行ってみましょう!!

コード

#!/bin/sh

#全ての引数
args="$*" #$*でコマンドライン引数全てを取得します。

#出力先(デフォルト)
output=output.txt

while [[ $# > 0 ]]; do # $#で与えた引数の数を取得します。

    case $1 in
        -h)
            echo ""
            echo "Usage: sh $0 -i input_file -n name [-o output]" #$0で実行スクリプト名を取得します。
            echo ""
            exit 0
            ;;
        -i)
            shift # shift で引数をずらす事ができます。
            # -f でファイルならtrue、 =~で正規表現で判別ができます。$1は1番目の引数です
            # ! をつけると 否定となります。
            # そのため1番目の引数が ファイルでかつ先頭に - がないならtrue となります。
            if [[ -f $1 && ! $1 =~ ^- ]];then
                input=$1
            else
                echo "読み込み可能なファイルを指定してください"
                echo "詳しくは -h を指定してください"
                exit 1
            fi
            ;;
        -n)
            shift
            # -n で 文字数があればtrueなので $1の文字数がありかつ先頭に - がないならtrueとなります。
            if [[ -n $1 && ! $1 =~ ^- ]];then
                name=$1
            else
                echo "名前を指定してください"
                echo "詳しくは -h を指定してください"
                exit 1
            fi
            ;;
        -o)
            shift
            if [[ -n $1 && ! $1 =~ ^- ]];then
                output=$1
            else
                echo "出力先を指定してください"
                echo "詳しくは -h オプションを指定してください"
                exit 1
            fi
            ;;
    esac
    shift
done

# 最後に必須オプションを指定します。
# はじめに判別をかけてもいいのですが、なんとなく後ろにしました。
if [[ ! "$args" =~ -i || ! "$args" =~ -n ]];then
    echo "必須オプションが未指定です。"
    echo "詳しくは -h オプションを指定してください"
    exit 1
fi

echo $input,$name,$output

そもそもオプション付きのスクリプトとは?

どういうものかというと

sh option.sh -i input_file -n name [-o output_file]

とかの -i input_file とか -o output_fileとかいうものです。

もちろんinput_fileや output_fileとして渡してあげても動くことは動きます。

ただ、ぱっと見分かりにくいですよね。何をしてくれるのかは分かりません。

sh convertText.sh hoge.txt hoge1.txt

テキストをConvertしてくれるシェルスクリプトだから何かを変えるスクリプトかな?

この引数に渡しているファイルはなんだろう。。

のように製作者以外が中身を見なければわからないものになってしまいます。

さらにみた人がスクリプトに対して知識がない人だともうギブアップです。

そのような問題を可能な限り解決するためにこれらのオプションを付与することは有効です。

例えば -i で 何かをinputするとか -o で何かを outputする というのは

なんとなく想像がつきます。

他の目的としたら製作者自身の自己満足になります

解説

一番掴みづらいのは shiftだと思います。

shiftを使うと引数を 1つずらす事ができます。

例えば

sh option.sh -i test.txt -n name -o output.txt

としたとき

  • $1 : -i
  • $2: test.txt
  • $3: -n
  • $4: name
  • $5: -o
  • $6: output.txt

としても取得する事自体は可能です。

ではこの後に追加で -u や -t オプションが追加されたらどうなりますか。

if 文の分岐が増えるし、やっぱ -i いらないやってなった時

コードの変更をしなくてはならないので改修が面倒です。

この問題を解決してくれるのが引数ずらしのshiftです。

これをするとどうなるのか。

コードを見ながら順に追っていくと

sh option.sh -i test.txt -n name -o output.txt

この状態で実行されると プログラムは次のように認識します。

  • $1 : -i
  • $2: test.txt
  • $3: -n
  • $4: name
  • $5: -o
  • $6: output.txt

引数は 6個なので $# = 6となります。($# > 0を満たします。)

ではそのままwhileに入ります。

最初にcase文で$1 を判別します。

今回は $1=-iであるので

        -i)
            shift 
            if [[ -f $1 && ! $1 =~ ^- ]];then
                input=$1
            else
                echo "読み込み可能なファイルを指定してください"
                echo "詳しくは -h を指定してください"
                exit 1
            fi
            ;;

の処理が該当します。

そして 入るとすぐにshiftがありますね。

すると先ほどの引数の先頭が削れてこうなります。

  • $1: test.txt
  • $2: -n
  • $3: name
  • $4: -o
  • $5: output.txt

最初に合った -iが削れて $1が1つ上にシフトされました。

そうなるとこのshift以降は$1=test.txtとなるので

$1=test.txtとして以降が処理されます。

今回は あらかじめtest.txtというファイルを作成しているため

if文がtrueとなり、 inputという変数にそのまま$1が代入されます。

そしてcaseを出た後にまたshiftがありますね。

するとどうなるか。

  • $1: -n
  • $2: name
  • $3: -o
  • $4: output.txt

となります。

あとはこれを引数がなくなるまで whileで繰り返すだけになります。

という仕組みを使うことによってオプション付きスクリプトを作成する事ができます。

例えば -m monthとして 月を指定したり、-d day として 日を指定できたら

日付をデータとして扱うようなスクリプトも作れてしまうかもしれません。

単純な shスクリプトですが、侮る事無かれです。

終わりに

いかがだったでしょうか。

人にしては大した事ないし、もしかしたらこれ使う?という代物かもしれません。

ですが、こういうものに興味があればぜひ色々作ってみてください。

必ず知識が身についてどこかで役に立つかもしれませんよ。

余談ですが私は2年前まで Dドライブすら知らない未経験者でした。

そんな僕でもできたので皆さんなら余裕です。

大丈夫です。一緒に頑張りましょう。僕も頑張ります。