Bashでちょっと凝ったオプションの解析をする

f:id:dojineko:20160630222829j:plain

痒いところに手が届かないのをなんとかしたかったのねん


Bashスクリプトを書いてたら、ちょっと凝ったオプション解析をしたくなった。

  • ロングオプションも使えるようにしたい
  • オプションの第二引数は任意にしたい

Bashスクリプトでオプション解析といえば getopt や getopts を使うと思いますが、

  • getopt はBashのビルトインコマンドなのでどこでも使えるけどロングオプションが使えない
  • getopts は BSD系(macOSなど) と GNU系(Linuxなど) で動きが違う

など微妙に痒いところに手が届かないので、下記のQiita記事を参考に、メモがてらちょっとアレンジしつつオレオレオプション解析を書いてみました。 qiita.com

下記をパースに使えば

  • ロングオプションが使える
  • 任意のオプションごとの引数を使える
  • オプション外の値を処理できる

・・・ようになりました

#!/bin/bash

PROGNAME="$( basename $0 )"

# Usage
function usage() {
  cat << EOS >&2
Usage: ${PROGNAME} [-h,--hoge] [--fuga [VALUE]] [--piyo VALUE]
  A sample script of parsing on bash.

Options:
  --hoge        A single option.
  --fuga        A option with optional value.
  --piyo        A option with required value.
  -h, --help    Show usage.
EOS
  exit 1
}

# オプションをパース
PARAM=()
for opt in "$@"; do
    case "${opt}" in
        '--hoge' )
            # 通常のオプションの場合
            HOGE=true; shift
            ;;
        '--fuga' )
            # オプションに続く値が 任意 の場合
            FUGA=true; shift
            if [[ -n "$1" ]] && [[ ! "$1" =~ ^-+ ]]; then
                FUGA_VALUE="$1"; shift
            fi
            ;;
        '--piyo' )
            # オプションに続く値が 必須 の場合
            if [[ -z "$2" ]] || [[ "$2" =~ ^-+ ]]; then
                echo "${PROGNAME}: option requires an argument -- $( echo $1 | sed 's/^-*//' )" 1>&2
                exit 1
            fi
            PIYO=true
            PIYO_VALUE="$2"
            shift 2
            ;;
        '-h' | '--help' )
            usage
            ;;
        '--' | '-' )
            shift
            PARAM+=( "$@" )
            break
            ;;
        -* )
            echo "${PROGNAME}: illegal option -- '$( echo $1 | sed 's/^-*//' )'" 1>&2
            exit 1
            ;;
        * )
            if [[ -n "$1" ]] && [[ ! "$1" =~ ^-+ ]]; then
                PARAM+=( "$1" ); shift
            fi
            ;;
    esac
done

# オプション無しの値を使う場合はここで処理する
X_VALUE="${PARAM}"; PARAM=("${PARAM[@]:1}")
Y_VALUE="${PARAM}"; PARAM=("${PARAM[@]:1}")
Z_VALUE="${PARAM}"; PARAM=("${PARAM[@]:1}")

[[ -z "${X_VALUE}" ]] && usage
[[ -z "${Y_VALUE}" ]] && usage
[[ -z "${Z_VALUE}" ]] && usage

# 規定外のオプションがある場合にはusageを表示
if [[ -n "${PARAM[@]}" ]]; then
    usage
fi

# 結果を表示
cat << EOS | column -t
HOGE ${HOGE:-false}
FUGA ${FUGA:-false}
FUGA_VALUE ${FUGA_VALUE}
PIYO ${PIYO:-false}
PIYO_VALUE ${PIYO_VALUE}
X_VALUE ${X_VALUE}
Y_VALUE ${Y_VALUE}
Z_VALUE ${Z_VALUE}
EOS

関連リンク

www.pakutaso.com qiita.com