#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:
- ARGV & ARGF - raw arguments
- GetoptLong - fashioned after the GNU getopt_long() C library
- OptionParser - more advanced and “Ruby-oriented” than GetoptLong
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