Top/article/Command-line-option-parser-for-bash
English | Japanese
English | Japanese

Menu

  • Top
  • Akihiko Yamaguchi 山口 明彦
  • Project プロジェクト
  • Text テキスト
  • Recent articles 最近の記事
  • Articles 記事一覧 (a to z)
  • Search 検索

Tags タグ †

  • [c++][bash][python][latex][php]
  • [linux][windows][mac][android]
  • [math][algorithm][idea][trick]
  • [liboctave][opencv][git][ros]
  • [setting][bug][general]
↑

Recent articles 最近の記事 †

2019-07-02
  • article/Display-Unix-Time
  • article/Synchronize-Linux-Time-to-Remote
2018-09-27
  • article/python-multimode-singleton
2018-09-02
  • article/rosinstall-git-default-remote
2017-07-28
  • article/SubMenu
2017-03-05
  • article/Import-a-different-version-of-OpenCV-in-Python
2015-08-17
  • article/DRC-finals-2015
2015-01-05
  • article/Upgrade-Android-to-Lollipop
2015-01-01
  • article/Kernel-panic-of-Linux-when-using-Xtion
  • article/Do-not-skip-freeing-data-when-using-tri-mesh-in-ODE
Access: 1/2066 - Admin
These search terms have been highlighted:[bash]

Command line option parser for bash

bash 用コマンドラインオプションパーサ

[bash]
2009-09-21

bash で使えるコマンドラインオプションパーサとしては,組み込みの getopts とかコマンド getopt が代表的だが,もっと手軽に使えるのはないものか.ということで作ってみた.

細かいことは置いておいて*1,まず使用例を.

使用例1 †

#!/bin/bash
source ~/lib/bash/option-parser.sh
parse_opts "$@" # パース

optx=`opt x 10`
opty=`opt y hoge`

# ...

2~3行目がパースを行っている部分, `opt x 10` は -x というオプションの値を取得する関数(10 はデフォルト値).

./test.sh -x 100

を実行すると, `opt x 10` は 100 を返し,`opt y hoge` はデフォルトの hoge を返す.ちなみに `opt y` のようにデフォルト値を省略した場合は空白を返す. 単純.

↑

使用例2 †

"-help" のような,単体で指定されるオプションもパースするには:

#!/bin/bash
source ~/lib/bash/option-parser.sh
set_single_opts help
parse_opts "$@" # パース

if [ `opt help 0` -eq 1 ]; then
  echo "つかいかた!"
  exit 1
fi

optx=`opt x 10`
opty=`opt y hoge`

# ...

3行目で,単体で指定されるオプションを指定している(複数でもOK).この場合,`opt help 0` はコマンドラインで指定されていれば 1 を,そうでなければ 0 を返す(デフォルト値の指定が無ければ空白).

./test.sh -x 100 -help

で「つかいかた!」と出力される.

↑

使用例3 †

rm コマンドの "rm -i a.txt b.txt" みたいなオプションをパースするには:

#!/bin/bash
source ~/lib/bash/option-parser.sh
set_single_opts help i
parse_opts "$@" # パース

echo "number of fargs=${#fargs[@]}"
echo "fargs[0]=${fargs[0]}"
echo "fargs[1]=${fargs[1]}"

ハイフンなしのコマンドライン引数("rm -i a.txt b.txt" の a.txt や b.txt)は,すべて fargs という配列に格納される.よって []${#fargs[@]}[] で要素数の取得(2)が,[]${fargs[0]}[] で最初の要素が取得(a.txt)できる.もちろん,for ですべての要素に対する処理も可能だ:

for ((i=0;i<${#fargs[@]};i++)); do
  echo "fargs[$i]=${fargs[$i]}"
done
↑

マニュアル †

option-parser.sh スクリプトで解析できるのは

  • single: -XX
  • pair: -XX YY
  • floating: YY

の3タイプのオプション.これらのオプションが,いくつでも,どんな順で並んでいてもよい.

まず,

source ~/lib/bash/option-parser.sh
set_single_opts AAA BBB
parse_opts "$@"

の順でパーサを実行する. set_single_opts は single (-XX) タイプのオプションを指定するコマンド(いくつでも指定可)で,指定しなければ pair (-XX YY) タイプのオプションとして認識されてしまう.

パーサを実行後,オプションの内容が以下のように取得できる.

  • pair (-XX YY) オプションは,`opt XX` で YY を取り出せる.コマンドラインで指定が無い場合,空白
  • single (-XX) オプションは,`opt XX` で 1 が返る.コマンドラインで指定が無い場合,空白
  • floating (YY) オプションは,配列 fargs に,出現順に YY が格納される

opt コマンド(関数)は opt OPTION_NAME DEFAULT のようにデフォルト値 DEFAULT を指定できる.この場合,コマンドライン引数にオプションが無ければ opt 関数は DEFAULT を返す(pair でも single でも同じ).

NOTE: オプション名の記号はすべてアンダーライン(_)と同一視される.例えば -x+y と -x-y はいずれも内部で -x_y に置き換えられる.

NOTE: -- 以降はすべて fargs に格納される(i.e. floating (YY) オプションとみなされる).ただし,-- が pair (-XX YY) オプションの YY に位置する場合,オプション -XX の内容として扱われる.

↑

スクリプトソース (option-parser.sh) †

  • 修正@Oct.14,2009: 一部の echo を printf -- に置き換えた(cf. echo -E トラブル).
  • 追加@Oct.14,2009: -- 以降はすべて fargs に格納されるようにした.
    #!/bin/bash
    # コマンドラインオプション解析スクリプト
    # 解析できるオプションは以下の3タイプ:
    #   single:   -XX
    #   pair:     -XX YY
    #   floating: YY
    # これらのオプションが,いくつでも,どんな順で並んでいてもよい
    # usage:
    #   source ~/lib/bash/option-parser.sh
    #   set_single_opts AAA BBB # single (-XX) オプションを指定
    #   parse_opts "$@" # パース
    # pair (-XX YY) オプションは,解析(parse_opts)後 `opt XX` で YY を取り出せる
    # single (-XX) オプションは, `opt XX` で 1 が返る (オプションで指定されていなければ空白)
    # single (-XX) のオプションを使う場合は, parse_opts を呼び出す前に set_single_opts を使う必要がある
    # floating (YY) オプションは,配列 fargs に YY が格納される
    # NOTE opt でコマンドライン引数にないオプションを取得しようとした場合 空白 "" が返る
    # NOTE オプション中の記号はすべてアンダーライン(_)と同一視される
    # NOTE -- 以降はすべて fargs に格納される(i.e. floating (YY) オプションとみなされる)
    
    # オプション名を内部名に変換する
    function option_name_conv() # OPTION_NAME
    {
      printf -- "$1" | sed 's/[^a-zA-Z0-9_]/_/g'
    }
    
    # OPTION_NAME の内容を取り出す
    # (何も格納されない場合は DEFAULT_VALUE が得られる)
    function opt() # OPTION_NAME [DEFAULT_VALUE]
    {
      eval "local res=\"\$commandline_option_`option_name_conv $1`\""
      if [ "$res" == "" ] && [ "$2" != "" ]; then printf -- "$2"; fi
      printf -- "$res"
    }
    
    # 単体で使われるオプション(-XX タイプ)を指定する
    function set_single_opts() # OPTION_NAME [OPTION_NAME [OPTION_NAME] ...]
    {
      local N=$#
      for ((i=0; i<$N; i++)); do
        eval "commandline_option_single_`option_name_conv $1`=1"
        shift
      done
    }
    
    # 使用する変数を指定する
    function using_opts() # OPTION_NAME [OPTION_NAME [OPTION_NAME] ...]
    {
      local N=$#
      for ((i=0; i<$N; i++)); do
        eval "commandline_option_used_`option_name_conv $1`=1"
        shift
      done
    }
    
    # 使われていないオプション(-XX YY タイプのみ)の一覧を出力する
    # 使われていないオプションがあれば 1, なければ 0 を返す
    function check_unused_opts()
    {
      local unused_opt_exists=0
      for on in ${commandline_option_list[@]};do
        eval "local used=\$commandline_option_used_`option_name_conv $on`"
        if [ $used -eq 0 ];then
          unused_opt_exists=1
          eval "res=\"\$commandline_option_`option_name_conv $on`\""
          echo "unused option: -$on $res"
        fi
      done
      return $unused_opt_exists
    }
    
    # すべての -XX YY タイプのオプションを出力
    function list_options()
    {
      for on in ${commandline_option_list[@]};do
        eval "local res=\"\$commandline_option_`option_name_conv $on`\""
        echo "$on=$res"
      done
    }
    
    function __register_opt()  # optname optcontents
    {
      local optname=$1
      local ioptname=`option_name_conv $1`
      local optcontents=$2
      local duplicated=0
      if [ $# -ne 2 ];then echo "invalid options for __register_opt! (bug!)"; fi
      eval "local oldcontents=\"\$commandline_option_$ioptname\""
      if [ "$oldcontents" != "" ]; then
        echo "warning: option -$optname is redefined (note: internal name the option is -$ioptname)"
        duplicated=1
      fi
      eval "commandline_option_$ioptname='$optcontents'"
      eval "commandline_option_used_$ioptname=0"
      if [ $duplicated -ne 1 ];then
        commandline_option_list[$commandline_option_count]=$optname
        commandline_option_count=$((commandline_option_count+1))
      fi
    }
    
    function __register_fargs()  # optcontents
    {
      if [ $# -ne 1 ];then echo "invalid options for __register_fargs! (bug!)"; fi
      fargs[$fargs_count]="$1"
      fargs_count=$((fargs_count+1))
    }
    
    function parse_opts()
    {
      local optname=''
      local commandline_option_count=0
      local fargs_count=0
      local flag_ign_opt=0
      local N=$#
      for ((i=0; i<$N; i++)); do
        if [ "$optname" == "" ];then
          if [ $flag_ign_opt -eq 1 ];then
            __register_fargs "$1"
          elif [ "$1" == "--" ];then
            flag_ign_opt=1
          elif [ "`printf -- $1|sed 's/^\-.\+//g'`" == "" ];then
            optname="`printf -- $1|sed 's/^\-//g'`"
            eval "local is_single=\$commandline_option_single_`option_name_conv $optname`"
            if [ "$is_single" == "1" ];then
              __register_opt $optname 1
              optname=""
            fi
          else
            __register_fargs "$1"
          fi
        else
          local optcontents=$1
          __register_opt $optname "$optcontents"
          optname=""
        fi
        shift
      done
      if [ "$optname" != "" ];then
        echo "incomplete option: -$optname"
        exit 1
      fi
    }
    

このソースのパス(上の例では ~/lib/bash/option-parser.sh)を source コマンドで呼び出して使う.


*1 本当は getopts とか getopt の存在を知らなかったという事実もほっておきます.ブログ書こうとして調べたら,すぐ見つかったよ.



Last-modified:2014-12-31 (Wed) 16:37:59 (3003d)
Link: article/Bash-cannot-echo-E(3004d) article/Script-to-generate-fade-in-out-videos(3004d)
Site admin: Akihiko Yamaguchi.
Written by: Akihiko Yamaguchi.
System: PukiWiki 1.5.0. PHP 5.2.17. HTML conversion time: 0.505 sec.