Fork me on GitHub

Project Notes

#370 Command Line Options with Ruby

Comparing all the various ways of parsing command line arguments with the Ruby standard library.

Notes

Building tools with ruby that run in a terminal generally requires handling command-line options and arguments.

There are a range of gems that offer improved or simplified CLI options handling, but I’ll just focus here on what is already provided by the rich standard library support.

What I’ll cover here:

NB: a few examples of third-party gems include:

  • https://rubygems.org/gems/getopt - The getopt library provides two different command line option parsers. They are meant as easier and more convenient replacements for the command line parsers that ship as part of the Ruby standard library
  • https://rubygems.org/gems/optparse-plus - provides a lot of small but useful features for developing a command-line app
  • https://rubygems.org/gems/cli-modular_options - facilitates modular application design by allowing you to declare CLI options in the context of a class or module and consume them in the context of an object instance using conventional ruby inheritance semantics
  • https://rubygems.org/gems/mixlib-cli - A simple mixin for CLI interfaces, including option parsing
  • https://rubygems.org/gems/gli - Build command-suite CLI apps that are awesome. Bootstrap your app, add commands, options and documentation while maintaining a well-tested idiomatic command-line app

ARGV & ARGF

If one just needs to grab 1 or 2 arguments without anything fancy, the ARGV global variable and ARGF class is all we really need. See ARGV & ARGF for library details.

Where this approach starts to fall down is when adding more than one optional parameter, or we have so many options that keeping the help/doc in line with the code starts to become a pain.

The ARGV global variable provides access to an array of options for us to do with as we wish. See argv_example.rb for a demonstration.

p ['ARGV', ARGV]
ARGV.each do |arg|
  case arg
  when '--help', '-h'
    puts 'Help: This is a simple example of using ARGV to handle command-line options.'
    puts 'Usage: ruby argv_example.rb [options]'
    puts 'Options:'
    puts '  --help, -h     Show this help message'
    puts '  --version, -v  Show version information'
    exit
  when '--version', '-v'
    puts 'Version 1.0.0'
    exit
  else
    puts "Unknown option: #{arg}"
  end
end

Example usage:

$ ./argv_example.rb --help
["ARGV", ["--help"]]
Help: This is a simple example of using ARGV to handle command-line options.
Usage: ruby argv_example.rb [options]
Options:
  --help, -h     Show this help message
  --version, -v  Show version information
$ ./argv_example.rb -v
["ARGV", ["-v"]]
Version 1.0.0

The ARGF class works with the array at global variable ARGV to make $stdin and file streams available in the Ruby program.

See argf_example.rb for a demonstration.

p ['ARGV', ARGV]
p ['ARGF.read', ARGF.read]

Example usage:

$ echo "Open the pod bay doors, Hal." | ./argf_example.rb
["ARGV", []]
["ARGF.read", "Open the pod bay doors, Hal.\n"]
$ ./argf_example.rb foo.txt bar.txt
["ARGV", ["foo.txt", "bar.txt"]]
["ARGF.read", "Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"]

GetoptLong (part of Ruby’s standard library)

This class is also based on the GNU getopt_long() C library call and offers similar functionality to the Getopt::Long from the getopt gem. It allows for both POSIX-style options (e.g., –file) and single-letter options (e.g., -f). See GetoptLong for library details.

See getoptlong_example.rb for a demonstration.

require 'getoptlong'
options = GetoptLong.new(
  ['--help', '-h', GetoptLong::NO_ARGUMENT],
  ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
  ['--output', '-o', GetoptLong::REQUIRED_ARGUMENT]
)
options.each do |option, arg|
  case option
  when '--verbose'
    puts "Verbose mode enabled."
  when '--output'
    puts "Output file: #{arg}"
  when '--help'
    puts "Usage: script.rb [options]"
    puts "--help, -h          Show this help message"
    puts "--verbose, -v       Enable verbose mode"
    puts "--output, -o FILE   Specify output file"
  end
end

Example usage:

$ ./getoptlong_example.rb --help
Usage: script.rb [options]
--help, -h          Show this help message
--verbose, -v       Enable verbose mode
--output, -o FILE   Specify output file
$ ./getoptlong_example.rb -v -o test.txt
Verbose mode enabled.
Output file: test.txt

OptionParser (part of Ruby’s standard library)

Considered more advanced and “Ruby-oriented” than GetoptLong. It provides a more flexible and declarative way to define options, automatically generate help messages, and handle argument type conversions. See OptionParser for library details.

OptionParser also supports a long list of Built-In Argument Converters, and it is possible to add custom converters.

See optparse_example.rb for a demonstration.

require 'optparse'
require 'optparse/date'

options = {}
OptionParser.new do |parser|
  parser.banner = "Usage: example.rb [options]"
  parser.on('-a', '--array=ARRAY', Array, 'provide a comma-separated list of values') # e.g., --array val1,val2,val3
  parser.on('-d', '--date=DATE', Date, 'provide a date (in the format YYYY-MM-DD)') # e.g., --date 2023-10-31
  parser.on('-v', '--[no-]verbose', 'Run verbosely')
  parser.on('-o', '--output FILE', 'Write to FILE')
  parser.on '-t', '--[no-]timeout [TIMEOUT]', Numeric, 'Timeout (in seconds) before timer aborts the run (Default: 1.8)' do |timeout|
    if timeout.is_a?(Numeric)
      timeout
    elsif timeout.nil?
      1.8
    elsif timeout == false
      -1
    end
  end
end.parse!(into: options)

puts "Options: #{options.inspect}"
puts "Array: #{options[:array].inspect}" if options[:array]
puts "Date: #{options[:date].inspect}" if options[:date]
puts "Output to #{options[:output]}" if options[:output]
puts "Timeout: #{options[:timeout].inspect}" if options[:timeout]
puts "Verbose output..." if options[:verbose]

Example usage:

$ ./optparse_example.rb --help
Usage: example.rb [options]
    -a, --array=ARRAY                provide a comma-separated list of values
    -d, --date=DATE                  provide a date (in the format YYYY-MM-DD)
    -v, --[no-]verbose               Run verbosely
    -o, --output FILE                Write to FILE
    -t, --[no-]timeout [TIMEOUT]     Timeout (in seconds) before timer aborts the run (Default: 1.8)

$ ./optparse_example.rb -v --no-timeout -o test.txt -a one,two,three -d 2025-10-02
Options: {:verbose=>true, :timeout=>-1, :output=>"test.txt", :array=>["one", "two", "three"], :date=>#<Date: 2025-10-02 ((2460951j,0s,0n),+0s,2299161j)>}
Array: ["one", "two", "three"]
Date: #<Date: 2025-10-02 ((2460951j,0s,0n),+0s,2299161j)>
Output to test.txt
Timeout: -1
Verbose output...
$ ./optparse_example.rb -t 12.34
Options: {:timeout=>12.34}
Timeout: 12.34

Credits and References

About LCK#370 ruby

This page is a web-friendly rendering of my project notes shared in the LittleCodingKata GitHub repository.

Project Source on GitHub Return to the LittleCodingKata Catalog
About LittleCodingKata

LittleCodingKata is my collection of programming exercises, research and code toys broadly spanning things that relate to programming and software development (languages, frameworks and tools).

These range from the trivial to the complex and serious. Many are inspired by existing work and I'll note credits and references where applicable. The focus is quite scattered, as I variously work on things new and important in the moment, or go back to revisit things from the past.

This is primarily a personal collection for my own edification and learning, but anyone who stumbles by is welcome to borrow, steal or reference the work here. And if you spot errors or issues I'd really appreciate some feedback - create an issue, send me an email or even send a pull-request.

Follow the Blog follow projects and notes as they are published in your favourite feed reader