コマンドラインオプションの解析

#はじめに
オプション解析用APIのgetopt_long()を使えばLinuxコマンドでよく見かけるような
` cat -n ` `cat --number`
などのコマンドオプション部分の解析を簡単に実装できます。

# オプションについて

* パラメータを取らないオプションとパラメータを取るオプションがある   
`ls -l` `head -n 3`
* パラメータを取らないオプションは一つにまとめれる  
`ls -laF`
* パラメータを取るオプションはそのパラメータをくっつけて書いて良い  
`head -n3`
* ロングオプション(マイナスが2つ)  
`--help --version`
* ショートオプション(マイナスが1つ)  
`-h -l -s`
* ロングオプションのパラメータはバラバラに書いても「=」を使って書いてもいい  
`--lines 3 --lines=3`

#getopt_long()の書式


```c
/* getopt_long(3) */

#include <getopt.h>

extern char *optarg*;
extern int optind;
extern int optopt;
extern int opterr;

struct option {
    const char *name;
    int has_arg;
    int *flags;
    int val;
};

int getopt_long(int argc, char * const *argv, 
                const char *optstring, 
                const struct option *longopts, 
                int *longindex);
```

#説明
&emsp;getopt_long()はオプション解析用のAPIです。
#####
&emsp;この関数は常にループと一緒に使います。呼び出すごとに発見したオプションの文字を返します。また、不明なオプションが渡された時には' ? 'を返します。オプションがなくなれば、-1が返されるので「 != -1」の条件でループを終了できます。

&emsp;次は、引数についてです。getopt_long()の最初の2つの引数はmain()の引数をそのまま渡します。
&emsp;第3引数には解析したいオプションを全てまとめて文字列として指定します。パラメータを取らないオプションを指定する際には、`-l -F -a`であれば "lFa"のように指定します。この時、順番は関係ありません。パラメータを取るオプションを指定する際には、そのオプションの文字の次にコロン( : )をつけます。例えば、`-n 3 -l`であれば、"n:l"のように指定します。この場合も、順番は関係ありません。
&emsp;第4引数には解析するオプションの定義を指定します。struct option構造体の配列を使ってロングオプションの使用を指示します。

| メンバ |値と意味 |
|:-:|:--|
|name   |ロングオプション名(help,number)   |
|has_arg   |no_argument(0) : パラメータを取らない<br>required_argument(1) : 必ずパラメータを取る<br> optional_argument(2) : パラメータをとっても、取らなくてもどちらでも良い   |
|flag   |NULL : getopt_long()はvalメンバの値を返す<br>NULL以外 : getopt_long()は0を返し、flagsにvalメンバの値を代入する   |
|val   |flagsメンバで指定されたところに返す値   |

&emsp;第5引数はNULLではない場合、発見したロングオプションに対応するstruct optionのインデックスを引数に指定した変数へ代入します。

&emsp;最後に、グローバル変数についての表です。

| 変数名 | 意味 |
|:-:|:--|
| optarg  |現在処理中のオプションパラメータ   |
| optind  | 現在処理中のオプションのargvでのインデックス  |
| optopt  | 現在処理中のオプション文字  |
| opterr  | 真の場合、エラー発生時にgetopt_long()がメッセージを表示  |


#例

```c:option.c
#include<stdio.h>
#include<stdlib.h>
#define _GNU_SOURCE
#include<getopt.h>

int lemon;

static struct option long_options[] = {
	{"apple" , no_argument      , NULL   , 'a'},
	{"peach" , no_argument      , NULL   , 'p'},
	{"orange", required_argument, NULL   , 'o'},
	{"grape" , optional_argument, NULL   , 'g'},
	{"lemon" , 0                , &lemon , 111},
	{0       , 0                , 0      ,  0 }
};

int main(int argc, char *argv[])
{
	int opt;
	int grape = 0;


	while((opt = getopt_long(argc, argv, "apo:g:", long_options, NULL)) != -1) {
		switch(opt) {
			case 'a':
				puts("apple");
				break;
			case 'p':
				puts("peach");
				break;
			case 'o':
				printf("orange = %s\n",optarg);
				break;
			case 'g':
				printf("grape = %s\n",optarg);
				break;
			case '?':
				fprintf(stderr,"optopt : %c\n",optopt);
				exit(1);
		}
	}
	if(lemon) printf("lemon = %d\n",lemon);
	printf("argc : %d optind : %d\n",argc,optind);
}
```

#####結果
-a

```bash
$ gcc option.c
$ ./a.out -a
apple
argc : 2 optind : 2
```
&emsp;getopt_long()に関連したグローバル変数optindには、オプション解析終了後のargvでのインデックスが格納されている。
&emsp;`argc == optind`の場合、オプションの他に何も引数として渡されてないことが分かる。

-ap

```bash
$ ./a.out -ap
apple
peach
argc : 2 optind : 2
```
--apple --peach

```bash
$ ./a.out --apple --peach
apple
peach
argc : 3 optind : 3
```
-o 3

```bash
$ ./a.out -o 3
orange = 3
argc : 2 optind : 2
```

-o3

```bash
$ ./a.out -o3
orange = 3
argc : 2 optind : 2
```

--orange=3

```bash
$ ./a.out --orange=3
orange = 3
argc : 2 optind : 2
```

--orange

```bash
$ ./a.out --orange
a.out: option `--orange` requires an argument
optopt : o
```
&emsp;orangeにはlong_optionsでrequired_argumentが指定されているのでパラメータをつけないとエラーが起こる。
この時、opterrが真になり関数がエラーメッセージを出力する。


--grape 3

```bash
$ ./a.out --grape 3
grape = (null)
argc : 3 optind : 2
```
&emsp;grapeにはlong_optionsでoptional_argumentが指定されている。上の結果はgrape = (null)となり、パラメータを取得できず、getopt_long()の解析対象に入っていない。一方、下の結果では、パラメータを「 = 」で代入するとgrape = 3となり、パラメータを取得できている。
&emsp;結果から、optional_argumentを指定した場合は、オプションに対してパラメータを「 = 」で代入しなければならない。

--grape=3

```bash
$ ./a.out --grape=3
grape = 3
argc : 2 optind : 2
```

--lemon

```bash
$ ./a.out --lemon
lemon = 111
argc : 2 optind : 2
```
&emsp;long_optionsのflagsがNULL以外のため、flagsに設定した&lemonにvalの値「111」が代入される。

設定されていないオプションが入力された場合、

```bash
$ ./a.out -h
a.out: invalid option -- h
optopt : h
```
&emsp;getopt_long()に関連したグローバル変数optoptにオプション文字が代入されているため、何が入力されたかを取得できる。

#おわりに
自分でlinuxコマンドオプションのような見た目を再現できるのは楽しいと思います。