Rubyのoptparseの使い方をわかりやすく解説

Rubyでコマンドライン引数を扱いたい場合、どのように実装したら良いでしょうか？
Rubyでは、そうした場合に使える標準ライブラリとして「optparse」というライブラリが用意されています。optparseを使えば、複雑なコマンドライン引数の定義と解析を簡単に行うことができます。
本記事では、optparseの基本的な使い方をどこよりも分かりやすく解説したいと思います。

## optparseの基本的な使用の流れ
Rubyのリファレンスを見ると、optparseを使用する流れについて以下のように書かれています。

1. OptionParserオブジェクトoptを生成する
2. オプションを取り扱うブロックをoptに登録する
3. opt.parse(ARGV) でコマンドラインを実際に解析する

これらのステップにより、コマンドライン引数の処理を簡単に実装することができるのですが、そもそもoptparseを初めて使用する人にとっては、この流れだけを見てもさっぱり分からないと思いますので、以下のサンプルコードを使って、各ステップの内容を具体的に説明していきます。

```ruby
require 'optparse'

options = {}
opt = OptionParser.new
opt.on("-n", "--name", "Your name") { |n| options[:name] = n }
opt.on("-a", "--age", Integer, "Your age") { |a| options[:age] = a }

opt.parse!(ARGV)

puts "Name: #{options[:name]}" if options[:name]
puts "Age: #{options[:age]}" if options[:age]
```

## オプションの定義と解析
### そもそもオプションとは
オプションとは、プログラムに渡す追加の情報のことです。例えば、プログラムを実行する際に、特定のファイルを指定したり、表示するメッセージを変更したりするために使います。オプションは通常、コマンドライン引数として渡されます。

### オプションを定義するとは
オプションを定義するというのは、プログラムがどのようなオプションを受け取ることができるかを指定することです。例えば、「-n」というオプションで名前を指定できるようにする、ということを定義します。

### オプションを解析するとは
オプションを解析するというのは、実際にコマンドラインから渡された引数をプログラムが読み取り、その値を使ってプログラムを制御することです。例えば、-n John と指定された場合、「John」という名前を読み取ってプログラム内で使用します。

### オプションの定義と解析を行うための準備
それではいよいよ、optparseの実装方法について解説していきます。先ほど掲載したサンプルコードを見ながら、以降の説明をご覧ください。

まずは、オプションの定義と解析を行うための準備をしましょう。
``` ruby
options = {}
```
この行では、コマンドライン引数の解析結果を格納するためのハッシュを作成しています。このハッシュに解析結果を格納することで、後で簡単にアクセスできるようになります。

```ruby
opt = OptionParser.new
```
続いて、この行ではOptionParserオブジェクトを生成しています。このオブジェクトに対してオプションを定義し、コマンドライン引数を解析する処理を行います。

### onメソッド
オプションを定義していきます。オプションを定義するにはonメソッドを使用します。
```ruby
opt.on("-a", "--age", Integer, "Your age") { |a| options[:age] = a }
```
onメソッドの各引数は以下の意味を持ちます。

・第1引数: 短縮オプションの指定（例: -a）。これは、短い形式でオプションを指定するためのものです。ユーザーはコマンドラインで -a と入力することで、指定したオプションの処理を利用できます。
・第2引数: 長形式オプションの指定（例: --age）。これは、より読みやすい形式でオプションを指定するためのものです。ユーザーはコマンドラインで --age と入力します。
・第3引数: オプションの引数に期待する値の型（省略可能、例: Integer）。この型は、引数がどのようなデータ型であるかを指定します。例えば、Integerの場合、引数は整数であることを指定しています。デフォルトの型は文字列になっています。
・第4引数: オプションの説明（例: "Your age"）。これは、ヘルプメッセージで表示される説明文です。ユーザーが --help（または-h） を使ったときに、この説明が表示されます。

### ブロック内の処理
onメソッドに渡されたブロックは、対応するオプションがコマンドライン引数に含まれている場合に実行されます。ブロック内の処理は、オプションの値を受け取り、それを適切な変数に格納するために使用されます。

例えば、以下のコードを見てみましょう：

```ruby
opt.on("-n", "--name", "Your name") {|n| options[:name] = n}
```
ここでのブロック { |n| options[:name] = n } は次のように動作します：

-n または --name オプションがコマンドライン引数に含まれている場合、このブロックが呼び出されます。
ブロック引数 n には、コマンドラインから渡された値が代入されます。
ブロック内でその値 n が options[:name] に格納されます。
このブロックの処理により、optionsハッシュにコマンドラインから渡された値が格納され、その後のスクリプトで使用できるようになります。

### オプションを解析する
オプションを定義した後、次のコードでコマンドライン引数を実際に解析します。

```ruby
opt.parse!(ARGV)
```
この行は、ARGVに含まれるコマンドライン引数を解析します。parse!メソッドを呼び出すと、optオブジェクトに定義されたオプションに従って、ARGV配列から引数を解析し、対応するブロックを実行します。
ARGVは、Rubyでプログラムが実行されたときにコマンドラインから渡される引数を格納する配列です。例えば、プログラムを次のように実行した場合、
```sh
ruby example.rb -n John -a 30
```
このとき、ARGVには["-n", "John", "-a", "30"]が格納されます。ARGVを使うことで、プログラムはコマンドライン引数にアクセスできます。

## 定義したオプションを使用する
オプションが解析された後、以下のコードで定義したオプションを実際に使用します。

```ruby
puts "Name: #{options[:name]}" if options[:name]
puts "Age: #{options[:age]}" if options[:age]
```
`puts "Name: #{options[:name]}" if options[:name]` は、options[:name] が存在する場合に、その名前を出力します。つまり、ユーザーが -n または --name オプションを指定していれば、その値が表示されます。
`puts "Age: #{options[:age]}" if options[:age]`も同様に、options[:age] が存在する場合に、その年齢を出力します。ユーザーが -a または --age オプションを指定していれば、その値が表示されます。

## 実際のコマンドラインの実行例
以上で、optparseによるオプションの定義と解析は完了です。
では、実際にコマンドラインからこのスクリプトを実行してみましょう。例えば、sample.rbというファイルに上記で示したスクリプトが記述してあるとします。このスクリプトは次のように実行します。

```sh
ruby sample.rb -n John
```
このコマンドを実行すると、スクリプトは次のように出力します。

```makefile
Name: John
```
もし、年齢も指定したい場合は、次のように実行します。

```sh
ruby sample.rb -n John -a 30
```
このコマンドを実行すると、スクリプトは次のように出力します。

```makefile
Name: John
Age: 30
```
以上が、コマンドラインからオプションを指定してスクリプトを実行する基本的な流れと方法になります。

## optparseの使い方をもっと知る
ここからは、もっとoptparseを使いこなすための内容になります。

### 任意の引数と必須の引数
オプションの引数には、任意のものと必須のものがあります。任意の引数はユーザーが指定しなくても良いもので、必須の引数は必ずユーザーが指定しなければならないものです。

#### 任意の引数
任意の引数を定義するには、オプションの引数を角括弧 [] で囲みます。例えば、-a [VAL] のように定義します。これにより、ユーザーが引数を指定しなかった場合でもスクリプトは正常に動作します。

```ruby
require 'optparse'

options = {}

opt = OptionParser.new
opt.on("-a [VAL]", "Optional value") do |val|
  options[:value] = val || "default value"
end

opt.parse!(ARGV)

puts "Value: #{options[:value]}"
```
このスクリプトでは、-a オプションが指定されていない場合、options[:value] には "default value" が代入されます。ユーザーが -a オプションを指定すると、その値が options[:value] に格納されます。

#### 必須の引数
必須の引数を定義するには、オプションの引数を角括弧で囲まずに定義します。例えば、-a VAL のように定義します。これにより、引数が指定されなかった場合はエラーとなります。

```ruby
require 'optparse'

options = {}

opt = OptionParser.new
opt.on("-a VAL", "Required value") do |val|
  options[:value] = val
end

opt.parse!(ARGV)

puts "Value: #{options[:value]}"
```
このスクリプトでは、-a オプションが引数なしで指定された場合、エラーメッセージが表示され、スクリプトは実行されません。ユーザーが -a オプションを指定すると、その値が options[:value] に格納されます。

### ブロックを利用したオプションの定義方法
ここまで、OptionParserオブジェクトを作成し、オプションをonメソッドのチェーンで定義する方法を説明してきましたが、以下のように、OptionParser.newにブロックを渡し、その中でオプションを定義することも可能です。

```ruby
require 'optparse'

options = {}
OptionParser.new do |opts|
  opts.on("-n", "--name NAME", "Your name") do |n|
    options[:name] = n
  end

  opts.on("-a", "--age AGE", Integer, "Your age") do |a|
    options[:age] = a
  end
end.parse!

puts "Name: #{options[:name]}" if options[:name]
puts "Age: #{options[:age]}" if options[:age]
```
ブロックを使った方法でも、オプションの定義や解析の流れは同じです。ブロック内でオプションを定義し、最後にparse!メソッドを呼び出してコマンドライン引数を解析します。

## おわりに
いかがだったでしょうか。私が最初にRubyのリファレンスマニュアルでoptparseの説明を読んだときに、非常に分かりにくいと感じたため、今回の記事では、Ruby初心者の人でも何とかついていける、そして半年後の自分が改めて読んでみてもoptparseの使い方がすんなり理解できることを意識して書いてみました。
optparseライブラリを使うことで、コマンドライン引数の解析を簡単に行うことができます。もしoptparseの使い方に迷った際は、こちらの記事の内容を参考にしてみていただければと思います。