連載
» 2020年02月14日 05時00分 公開

Linux基本コマンドTips(379):【 getopt 】コマンド――オプションを解析する

本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回は、シェルスクリプト内などでオプションを解析する「getopt」コマンドです。

[西村めぐみ,@IT]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「Linux基本コマンドTips」のインデックス

Linux基本コマンドTips一覧

 本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回は、シェルスクリプト内などでオプションを解析する「getopt」コマンドです。

getoptコマンドとは?

 「getopt」は、シェルスクリプト内などでオプションを解析するコマンドです。自作のシェルスクリプトで「-a」のような「ハイフン+アルファベット1文字」のオプションを扱う際に便利です。「-f ファイル名」のように引数を取るオプションも解析できます。

 よく似た名前で用途も同じ「getopts」コマンドとはどこが異なるのでしょうか(連載第378回)。まず、getoptsコマンドはbashのビルトインコマンドです。さらにgetoptsコマンドでは、「--」から始まるロングオプションを解析できません。

 getoptコマンドは外部コマンド(/usr/bin/getopt)であり、ロングオプションの解析も可能です。

 なお、getoptは、CentOS 8やUbuntu 18などに収録されているGNU版(util-linuxパッケージ収録、※1)の他に、macOSなどに収録されているBSD版が存在します。BSD版のgetoptは空白や特殊記号を含む引数をうまく扱えないという問題があるので注意してください(※2)。getoptコマンド以外の手段を検討した方がよいかもしれません。以下の解説はGNU版(CentOS 8版)のgetoptコマンドを使用しています。

※1 どの版を利用しているのかを調べるには「getopt --verion」を実行する。
※2 BSD版のmanには、この問題の解決が難しいことを次のように記している。「Arguments containing white space or embedded shell metacharacters generally will not survive intact; this looks easy to fix but isn't. People trying to fix getopt or the example in this manpage should check the history of this file in FreeBSD.」



 この他、オプションを解析する際には、引数を1つずらす「shift」コマンドを使うことも可能です。



コマンドの書式

getopt [オプション] オプション文字列 引数

getopt [オプション] -o オプション文字列 -- 引数

getopt [オプション] -o オプション文字列 -l 長いオプション -- 引数

※ [ ]は省略可能な引数を示しています。




getoptの主なオプション

短いオプション 長いオプション 意味
-o オプション --options オプション オプションとして使用したい文字を指定する(本文を参照
-l オプション --longoptions オプション 長いオプションとして使用したい文字列を指定する(-oと併用、複数ある場合は「,」で区切るか「-l」オプションを複数回使用する)
-a --alternative 単一の「-」で始まるロングオプションを許可する
-s シェルの種類 --shell シェルの種類 使用するシェルを「sh」「bash」「csh」「tcsh」から指定する(デフォルトはbash、※3)
-n --name 名前 getoptを使うプログラムの名前を指定する(エラーメッセージを表示する際に使用する)
-q --quiet エラーメッセージを表示しない
-Q --quiet-output 通常の出力を省略する
-u --unquoted 出力にクオートを付けない

※3 シェルによってエスケープの処理が異なるため、bash(デフォルト)以外のシェルスクリプトでgetoptコマンドを使用する場合に指定する。なお、zshの場合、「zparseopts」コマンド(man zshmodules参照)で長いオプションを解析可能。





短いオプションを解析する

 「getopt オプション文字列 引数」または「getopt -o オプション文字列 -- 引数」で、引数の部分を解析した結果を表示します(画面1)。「-o」を使うと、引数の引用符を考慮します。

 例えば、オプションとして「-a」「-b」「-c」を使用したい場合は「getopt abc」または「getopt -o abc」のようにします。

コマンド実行例

getopt abc 解析したい文字列

(短いオプションとして「-a」「-b」「-c」を使うコマンドラインを解析する)

getopt -o abc -- 解析したい文字列

(短いオプションとして「-a」「-b」「-c」を使うコマンドラインを解析する)


画面1 画面1 短いオプションを解析したところ コマンドラインからgetoptコマンドを使った例

 画面1の最初のコマンドラインでは、短いオプションとして「-a」「-b」「-c」を使うように指定(abc)しています。このとき、コマンドラインから「-a -bc test "s1 s2"」を入力した場合、どのように解析するのかが分かります。出力にある「--」はオプションと引数の区切りを意味しています。

 2番目のコマンドラインでも同じ短いオプションを指定(-o abc)しています。最初のコマンドラインと同じく「-a -bc test "s1 s2"」と入力した場合でも、解析結果が多少異なっていることが分かります。これは「-o」を使ったことで、オプションの引数で使用している引用符を考慮するようになったためです。

 指定していないオプションを入力した場合、エラーメッセージを表示します(画面2)。ここで「-q」を指定すると、エラーメッセージを出力しません。

画面2 画面2 指定していないオプションを入力したところ


スクリプト内でオプションを解析する

 スクリプト内でgetoptコマンドを使用した場合、getoptコマンドの実行結果を変数に保存し、その変数を利用するという形になります。

 スクリプト1(opt4.sh)では、まず、「OPTIONS=`getopt -o abc -- "$@"`」で、コマンドラインから受け取った内容を解析し、その結果を変数「OPTIONS」に保存しています。

 続く、「eval set -- "$OPTIONS"」で、変数「OPTIONS」の内容、つまり解析結果をコマンドラインから受け取ったものとして、while文を実行しています。このようなwhile文の使い方については「シェルスクリプトに挑戦しよう(15)」の「引数を順番に処理する」を参照してください。

#! /bin/bash
OPTIONS=`getopt -o abc -- "$@"`
eval set -- "$OPTIONS"
while [ $# -gt 0 ]
do
  case $1 in
    -a) echo "-aが設定されている";;
    -b) echo "-bが設定されている";;
    -c) echo "-cが設定されている";;
    --) shift; break;;
  esac
  shift
done
while [ $# -gt 0 ]
do
  echo "引数=$1"
  shift
done
スクリプト1(opt4.sh)

 opt4.shを実行した結果は画面3の通りです。

画面3 画面3 スクリプト内でオプションを解析したところ

 opt4.shを実行すると、きちんと動作しますが、対象外のオプションを指定してスクリプトを実行した場合、画面3のように実行結果に「getopt: 無効なオプション」というメッセージを表示してしまいます。

 getoptという文字列の代わりにスクリプト名を表示したい場合、「-n $0」を指定します。「$0」には実行時のコマンド名(ここではシェルスクリプト名)が入っています。パス名を取り除きたい場合は「-n `basename $0`」のようにしますが、「``」の中で使用したいので、「$()」を使うとよいでしょう。具体的には次のように冒頭部分を修正します。実行すると先ほどのメッセージを改善できました(画面4)。

#! /bin/bash
OPTIONS=`getopt -n $(basename $0) -o abc -- "$@"`
eval set -- "$OPTIONS"
# (以下、修正前と同じ)
スクリプト2(修正版のopt4.sh)
画面4 画面4 エラーメッセージにシェルスクリプトの名前を表示するように修正した結果


引数付きの短いオプションを解析する

 「-f ファイル名」のように、オプションに続いて引数を使いたい場合は、オプション文字列の該当する文字の後ろに「:」を付けます(画面5)。

 ただし、「:」を使うと引数に相当する文字列を必ず探し出して解析するため、意図とは異なった動作になる場合があります(画面6)。「引数があってもなくてもよい」としたい場合は長いオプションを使用するとよいでしょう(連載第380回)。

コマンド実行例

getopt a:bc: 解析したい文字列

(短いオプションとして「-a」「-b」「-c」を使い、-aと-cが必ず引数を取る場合のコマンドラインを解析する)

getopt -o a:bc: -- 解析したい文字列

(短いオプションとして「-a」「-b」「-c」を使い、-aと-cが必ず引数を取る場合のコマンドラインを解析する)


画面5 画面5 引数付きの短いオプションを解析したところ

 画面5では、まず短いオプションとして「-a」「-b」「-c」を使い、「-a」と「-c」は引数が必要であると指定して、コマンドラインから「-a aaa -bc test "s1 s2"」を入力した場合にどのように解析するかを確認しています。2番目のコマンドラインも同様です。3番目のコマンドラインではわざと「-b」を指定していません。いずれも意図通りの結果となりました。

画面6 画面6 引数付きの短いオプションを解析したところ オプションを引数だと解析してしまった

 画面6では意図とは異なった解析結果になりました。「-bc」を「-a」の引数と見なしています。これは引数付きと指定したオプション(-a)の次の文字列を、必ずオプションの引数として扱うためです。



シェルスクリプトで応用する

Copyright © ITmedia, Inc. All Rights Reserved.

編集部からのお知らせ

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。