コマンドライン引数を扱う際の注意点(ARGV.getopts)
フィヨルドブートキャンプではRubyのプラクティスの一つに「Rubyでlsコマンドをつくる」という課題がある。
その課題作成中にコマンドライン引数を扱う際につまずいたことのメモ。
やりたいこと
ls.rbコマンドのオプションとディレクトリ名を引数として受け取りたい。
コマンド:$ ls.rb -alr <ディレクトリ名>
参照したこと
ARGVとは
ARGV
をプログラム中に呼び出すことでコマンドライン引数を配列で受け取ることができる。
Object::ARGV (Ruby 2.7.0 リファレンスマニュアル)
サンプル
- プログラム
p ARGV
- 実行シェルコマンド
$ ruby sample.rb foo bar baz ["foo", "bar", "baz"]
ARGVでオプションを取得するには
optparse を require することにより、OptionParser::Arguable の機能を使うことができる。
ARGV.getops(short_opt, *long_opt)でオプションを保持したHashを得ることができる。
OptionParser::Arguable#getopts (Ruby 2.7.0 リファレンスマニュアル)
サンプル
- プログラム
#!/usr/bin/env ruby require 'optparse' params = ARGV.getopts('a:b:', 'foo', 'bar:') p params
- 実行結果
$ ruby sample.rb -a 1 --foo --bar xxx {"a"=>"1", "b"=>nil, "foo"=>true, "bar"=>"xxx"}
試したことと分かったこと
まずp ARGV
でどのような値が取得できるか見てみる。
#!/usr/bin/env ruby require 'optparse' p ARGV
$ ls.rb -a ["-a"] $ ls.rb -arl ["-arl"] $ ls.rb -a test ["-a", "test"]
配列で取得できた。
次はオプションを取得するためにgetoptsを使ってみる。
~省略~ p ARGV.getopts('alr')
$ ls.rb -a {"a"=>true, "l"=>false, "r"=>false} $ ls.rb -arl {"a"=>true, "l"=>true, "r"=>true} $ ls.rb -a test {"a"=>true, "l"=>false, "r"=>false}
オプションは取得できたが、オプションの後のディレクトリ名は取得できない。
getoptsの後にARGVを取得してみる。
$ ls.rb -a test
を実行すれば {"a"=>true, "l"=>false, "r"=>false, "test"}
のように取得できるのではないか。
~省略~ p ARGV.getopts('alr') p ARGV
$ ls.rb -a test {"a"=>true, "l"=>false, "r"=>false} ["test"]
引数は取得できたが、予想に反してtestだけだった。 試しにgetoptsの前でもARGVを参照してみる。
~省略~ p ARGV p ARGV.getopts('alr') p ARGV
$ ls.rb -a test ["-a", "test"] {"a"=>true, "l"=>false, "r"=>false} ["test"]
どうやら、AGRV.getopts
を実行すると、ARGVから取得した値を削除する模様。
るりまには「破壊的な変更」のような記載はないけど、自身をパースがこのような挙動の説明になるっぽい……?
今回は以下のようにAGRV.getopts
でオプションの引数を取得した後にAGRV
をディレクトリ名として取得することで解決した。
options = ARGV.getopts('alr') input_dir = ARGV