Bash
Mac
Linux

getopt(GNU/Linux, BSD) かgetopts(bash builtin) かで迷わない、bash のオプション解析

bash スクリプトでオプション解析を行う場合の悩み

bash でスクリプトでオプション解析を行う時に、getopt(GNU/Linux, BSD) とgetopts(bash ビルトイン)を利用することができますが、以下のような悩みがあります。

  • getopt

    • GNU/Linux 版とBSD 版で挙動が違う
    • GNU/Linux 版ではロングオプションが利用できるがBSD 版ではできない
    • GNU/Linux 版では引数(arg)のあとにオプションを置いても引数とオプションを分けて解析できるがBSD 版ではできない
  • getopts

    • bash であれば、あらゆるOS 上で利用できる
    • ロングオプションは利用できない
    • 引数のあとにオプションを置いても引数とオプションを分けて解析できない

上記の内容を考慮して、bash で少し複雑なオプション解析を必要とするスクリプトを組む場合にはgetopt(GNU/Linux) を使用したいのですがそれを使用した場合、Mac 等のLinux 環境外では利用できず不便なものとなります。
であればbash 環境下であればあらゆるOS 上で使用できるgetopts を使用したいのですが、それではロングオプションへの対応や引数のあとにオプションを置いた場合に解析できない問題がつきまとってくることになります。

そこで、上記の問題を解決するためにbash 環境であれば共通的に利用できるgetopt(GNU/Linux) があればこの問題を解決できるのではないか!
ということでbash ビルトインとして、bash 環境であれば共通で使えるgetopts を使ってgetopt(GNU/Linux) を実現できるユーテリティを作成しました!という記事です。

リポジトリ

※Bash 3 系では正しく動きません

使い方

リポジトリのgetoptses.sh をロードしてgetoptses コマンド(getopt にses がついている)をgetopt(GNU/Linux) と同じように使用すれば良いだけです。
なお、getopt にはオプションが幾つかありますが、getoptses ではよく使われる-o, --options-l, --long オプションのみを実装しています。

gitcloneからgetoptsesのロード
$ git clone https://github.com/TsutomuNakamura/getoptses.git
$ cp getoptses/getoptses.sh /path/to/getoptses/
$ . /path/to/getoptses/getoptses.sh

もしくは直接getoptses.sh をロードすることもできます。

curlでgetoptsesのロード
$ . <(curl -Sso- https://raw.githubusercontent.com/TsutomuNakamura/getoptses/master/getoptses.sh)

オプション解析のサンプルスクリプトは以下のようになります。

getoptsesサンプル
#!/bin/bash

function main() {
    . /path/to/getoptses/getoptses.sh

    local options
    options=$(getoptses -o "ab:" --longoptions "long-a,long-b:,lonb-c" -- "$@")
    if [[ "$?" -ne 0 ]]; then
        echo "Invalid option were specified" >&2
        return 1
    fi
    eval set -- "$options"

    while true; do
        case "$1" in
        -a | --long-a )
            echo "-a, --long-a"
            shift
            ;;
        -b | --long-b )
            echo "-b, --long-b: $2"
            shift 2
            ;;
        --long-c )
            echo "--long-c"
            shift
            ;;
        -- )
            shift
            break
            ;;
        * )
            echo "Internal error has occured" >&2
            return 1
            ;;
        esac
    done

    echo "args: $@"
    return 0
}

main "$@" || exit $?

参考