Home > Archives > 2016-10
2016-10
shell scriptでlong optionを処理する
- 2016-10-16 (日)
- 未分類
仕事でシェルスクリプトを時々書く。
オプション処理にはgetoptsを使っている。
オプションの種類が増えてくると
意味が分かりやすいロングオプションが使いたいくなるが
getoptsではショートオプションしか使えないらしい。
こちらの記事や
bash によるオプション解析
こちらの記事が
bashでロングオプションとショートオプションの両方に対応する
とても参考になり、やり方を考えてみた。
環境依存なgetoptはなるべく使いたくないし
自作せずに既存の方法をなるべく使って実現したくなる。
こんな感じかなぁ。。
ポイントはロングオプションが指定されたらoptとOPTARGを書き換えて
それ以降の処理をショート/ロングオプション共通にすること。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#!/bin/bash check_arg_exit(){ # オプションの引数がなかったら終了 if [ -z "$OPTARG" ]; then echo "${0##*/}: option needs arg -- $opt" exit 1 fi } while getopts "hf:-:" opt; do if [ $opt = "-" ]; then opt=`echo ${OPTARG} | awk -F'=' '{print $1}'` OPTARG=`echo ${OPTARG} | awk -F'=' '{print $2}'` fi case "$opt" in h | help) echo "help" exit 0 ;; f | file) check_arg_exit echo "target file name is ${OPTARG}." ;; ? ) exit 1 ;; # ここはgetoptsのエラー出力に任せる * ) echo "invalid option -- $opt" exit 1 ;; # 未定義のlong optionのエラーはここで出力 esac done shift $((OPTIND - 1)) echo "main process" exit 0 |
必須のオプション引数が指定されなかった時に
エラーを表示する方法がないので自前で「check_arg_exit」を定義しておき
必要なcase節に書いておく。
全部自前の「check_arg_exit」に統一しようと思って
getoptsのオプション指定の先頭にコロンを足して
エラー表示をしないようにしたのだけど
1 |
while getopts ":hf:-:" opt; do |
これだとfオプションが引数無しで使われたときにcase文が?節に落ちる。
未定義のショートオプションを使った場合も?節に落ちるので
エラー表示を「オプション引数がない」にすればよいのか
「未定義のオプション」にすればいいのかわからないので諦めて
ショートオプションのエラー表示は全部getoptsに任せることにする。
(2016/10/16訂正)
man bashを読むとgetoptsは「silent」(オプション指定の先頭がコロン)なら
必須引数無は「:」、未定義オプションは「?」がoptにセットされるので区別可能。
「not silent」ならいずれも「?」がoptにセットされるらしい。
でもこのときoptが「:」や「?」になっていて
元のオプション文字はOPTARGに入っているので
自前のエラー処理と共通化できない。
ので、結局ショートオプションのエラー表示はgetoptsにお任せ。
未定義のロングオプションが指定されたら
エラー表示したいのでcase文の最後のアスタリスク節は必要。
その前の?節が無いと、未定義のショートオプションを指定した時に
getoptsからエラー表示が出てアスタリスク節でも自前のエラー表示も出てしまうので必要。
1 2 3 |
$ ./longopt.sh -a ./longopt.sh: 不正なオプションです -- a invalid option -- ? |
これでやりたいことは全部できてそう。
・ロングオプションを処理できる
・ロングオプションに引数指定できる
・未定義オプションでエラーを表示
・必須のオプション引数が無いときにエラー表示
あと出来てないのはこれぐらいだろうか。
私の用途ではこれらの要件は不要なので、もう満足です。
・通常引数の後にオプション指定
・引数があってもなくてもよいロングオプション
主な実行結果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
$ longopt.sh main process $ longopt.sh -h help $ longopt.sh --help help $ longopt.sh -f /home/UserName/mysh/longopt.sh: オプションには引数が必要です -- f $ longopt.sh -ffilename target file name is filename. main process $ longopt.sh -f filename target file name is filename. main process $ longopt.sh --file longopt.sh: option needs arg -- file $ longopt.sh --file=filename target file name is filename. main process $ longopt.sh -a /home/UserName/mysh/longopt.sh: 不正なオプションです -- a $ longopt.sh --hoge invalid option -- hoge |
- Comments: 0
- Trackbacks: 0
cutコマンドでデリミタ(区切り文字)の連続を1つの区切りにできない
- 2016-10-10 (月)
- 未分類
cutコマンドでは、デリミタの連続を1つの区切りとして扱えない。
できるオプションがあるのだろうと思って調べてみたが、
どうもそういう機能はなさそうで
sedで区切り文字を1つにまとめてからcutするか、
cutじゃなくてawkを使うのが一般的の様子。
一番使うコマンドを調べるというよくある例。
1 |
history | awk '{print $2}' | sort | uniq -c | sort -nr |
確かにこれでやりたいことできるし、
awkは覚えておくといろいろ役に立つのだけど、
cutのようにただ列を抽出したいだけの用途の場合、
awk ‘print {$2}’
をタイプすることすらメンドクサイ。
mcut.shというシェルスクリプトを書いておきます。
(名前のセンスはないけど、my cut コマンド)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#!/bin/bash if [ ! -p /dev/stdin ]; then #入力がパイプじゃないときは何もしない exit 1 fi echo $@ | grep -q -e "^[0-9 ]*$" if [ $? -ne 0 ]; then #引数が数字とスペースでないときは引数フォーマットエラー echo "引数に使えるのは数字のみです" exit 1 fi AWK_OPT=`echo $@ | sed -e 's/\([^ ]\+\)/$\1/g' -e 's/ \+/,/g'` cat - | awk "{print $AWK_OPT}" exit 0 |
これで先ほどの例はこれで書ける。
1 |
history | mcut.sh 2 | sort | uniq -c | sort -nr |
- Comments: 0
- Trackbacks: 0
Home > Archives > 2016-10
- Search
- Feeds