golangの日記

Go言語を中心にプログラミングについてのブログ

Shellscript コマンドライン引数のパース(Bash)

shellscript.png


Shellscript でコマンドライン引数をパースしたい。※注意: fish は勿論 sh は実行不可で bash のみ動作します





変数のスコープ

前提として変数のスコープを理解しておく。

#!/bin/bash

func-echo() {
    echo "$VarA"
    local VarB="Variable B"
    VarC="Variable C"
}

func-main() {
    local VarA=""

    func-echo # 空文字

    VarA="Variable A"

    func-echo # Variable A      func-echo 内から変数 VarA を呼び出すことができる

    echo "$VarB" # 空文字        func-echo 内のローカル変数にはアクセス不可

    echo "$VarC" # Variable C   func-echo 内のグローバル変数にはアクセス可能
}

func-main



shift の使い方

shift で引数(配列)の先頭を削除する。$# は長さ(length)

#!/bin/bash

set foo bar baz

func-main() {
    echo "$@" # foo bar baz

    echo "$1" # foo
    echo "$2" # bar
    echo "$3" # baz

    echo "$1" # foo
    echo "$#" # 3

    shift

    echo "$1" # bar
    echo "$#" # 2

    shift

    echo "$1" # baz
    echo "$#" # 1
}

func-main "$@"



引数をパースするコード

#!/bin/bash

func-usage() {
    cat <<EOS

Usage: $NAME [options] [arguments]

Options:
  -h, --help        Display this help and exit.
  -v, --version     Output version information and exit.
  -n, --no-required Arguments no required.
  -r, --required    Arguments required.
  -o, --optional    Argument is optional.

EOS
    exit 2
}

func-version() {
    echo "$NAME: $VERSION"
    exit 2
}

func-error() {
    echo "Error: $1." 1>&2
    exit 1
}

func-parse() {
    local skip=0
    # 値を必要としないオプション
    opt_no_required=false
    # 値が必須なオプション
    opt_required=false
    # 値が任意なオプション
    opt_optional=false

    # $@ の長さが 0 になるまでループする
    while (($# > 0)); do
        case "$1" in
        -n | --no-required) # 値を必要としないオプション
            opt_no_required=true
            skip=1
            ;;
        -r | --required) # 値が必須なオプション
            # 値が無いまたは - から始まる値はエラーにする
            if [[ -z "$2" ]] || [[ "$2" =~ ^-+ ]]; then func-error "'$1' options require arguments"; fi
            opt_required="$2"
            skip=2
            ;;
        -o | --optional) # 値が任意なオプション
            opt_optional=true
            skip=1
            # 値があるか - で始まらない場合は値をセットする
            if [[ -n "$2" ]] && [[ ! "$2" =~ ^-+ ]]; then
                opt_optional="$2"
                skip=2
            fi
            ;;
        -v | --version)
            func-version
            ;;
        -*)
            func-usage
            ;;
        esac

        case "$skip" in
        1)
            # 変数 skip が 1 の場合は値がないオプション
            skip=0
            shift
            ;;
        2)
            # 変数 skip が 2 の場合は値を指定したオプション
            skip=0
            shift 2
            ;;
        *)
            # 変数 skip が 0 の場合は残りの引数として配列 remain に入れておく
            remain+=("$1")
            shift
            ;;
        esac
    done
}

func-main() {
    local NAME="command"
    local VERSION="v0.1"

    local -a remain=()

    func-parse "$@"

    echo "required   : $opt_required"
    echo "no required: $opt_no_required"
    echo "optional   : $opt_optional"
    echo "remain args: ${remain[*]}"
}

func-main "$@"


実行結果

$ ./command.sh -n foo -r bar baz -o
required   : bar
no required: true
optional   : true
remain args: foo baz
$ ./command.sh --version
command: v0.1

$ ./command.sh --help

Usage: command [options] [arguments]

Options:
  -h, --help        Display this help and exit.
  -v, --version     Output version information and exit.
  -n, --no-required Arguments no required.
  -r, --required    Arguments required.
  -o, --optional    Argument is optional.