Parsing Command-Line Arguments

Problem

You want to make your Ruby script take command-line arguments, the way most Unix utilities and scripts do.

Solution

If you want to treat your command-line arguments as a simple list of strings, you can just iterate over the ARGV array.

Heres a Ruby version of the Unix command cat; it takes a list of files on the command line, opens each one, and prints its contents to standard output:

	#!/usr/bin/ruby -w
	# cat.rb

	ARGV.each { |filename| IO.readlines(filename).each { |line| puts line } }

If you want to treat your command-line arguments as a list of files, and you plan to open each of those files and iterate over them line by line, you can use ARGF instead of eARGV. The following cat implementation is equivalent to the first one.[3]

[3] Its actually a little better, because ARGF will iterate over standard input if there are no files given in ARGV.

	#!/usr/bin/ruby -w
	# cat_argf.rb

	ARGF.each { |line| puts line }

If you want to treat certain command-line arguments as switches, or as anything other than a homogenous list of strings, use the OptionParser class in the optparse library. Don write the argument parsing code yourself; there are too many edge cases to think about.

Discussion

The OptionParser class can parse any command-line arguments you e likely to need, and it includes a lot of Unix know-how that would take a long time to write yourself. All you have to do is define the set of arguments your script accepts, and write code that reacts to the presence of each argument on the command line. Here, Ill use OptionParser to write cat2.rb, a second Ruby version of cat that supports a few of the real cats command-line arguments.

The first phase is turning any command-line arguments into a data structure that I can easily consult during the actual program. The CatArguments class defined below is a hash that uses OptionParser to populate itself from a list of command-line arguments.

For each argument accepted by cat2.rb, Ive added a code block to be run as a callback. When OptionParser sees a particular argument in ARGV, it runs the corresponding code block, which sets an appropriate value in the hash:

	#!/usr/bin/ruby
	# cat2.rb
	require optparse

	class CatArguments < Hash
	 def initialize(args)
	 super()
	 self[:show_ends] = \

	 opts = OptionParser.new do |opts|
	 opts.banner = "Usage: #$0 [options]"
	 opts.on(-E, --show-ends [STRING],
	 display [STRING] at end of each line) do |string|
	 self[:show_ends] = string || $
	 end

	 opts.on(-n, --number, 
umber all output lines) do
	 self[:number_lines] = true
	 end

	 opts.on_tail(-h, --help, display this help and exit) do
	 puts opts
	 exit
	 end
	 end

	 opts.parse!(args)
	 end
	end

	arguments = CatArguments.new(ARGV)

At this point in the code, our CatArguments object contains information about which command-line arguments were passed in. If the user passed in a command-line switch -E or --show-ends, then arguments[:show_ends] contains a string to be shown at the end of each line.

Whats more, the command-line arguments handled by OptionParser have been stripped from ARGV. The only things left in ARGV can be assumed to be the names of files the user wants to concatenate. This means we can now use the ARGF shortcut to iterate over those files line by line. All we need is a little extra code to actually implement the command-line arguments:

	counter = 0
	eol =
	ARGF.each do |line|
	 line.sub!(/$/, 
arguments[:show_ends])
	 print \%6.d  % (counter += 1) if arguments[:number_lines]
	 print line
	end

Heres a shell session showing off the robustness that optparse brings to even a simple script. The help message is automatically generated, multiple combined flags are handled correctly, nonexistent flags are rejected, and you can disable flag processing altogether with the -- argument. In general, it works like you expect a Unix command-line tool to work.

	$ ./cat2.rb --help
	Usage: ./cat2.rb [options]
	 -E, --show-ends [STRING] display STRING at end of each line
	 -n, --number number all output lines
	 -h, --help display this help and exit

	$ ./cat2.rb file1 file2
	This is file one.
	Another line in file one.
	This is file two.
	Im a lot more interesting than file one, Ill tell you that!

	$ ./cat2.rb file1 -E$ -n file2
	 1 This is file one.$
	 2 Another line in file one.$
	 3 This is file two.$
	 4 Im a lot more interesting than file one, Ill tell you that!$

	$ ./cat2.rb --nosuchargument
	/usr/lib/ruby/1.8/optparse.rb:1445:in `complete: invalid option: --nosuchargument
	(OptionParser::InvalidOption)

	$ ./cat2.rb --show-ends=" STOP" -- --argument-looking-file
	The name of this file STOP
	looks just like an argument STOP
	for some odd reason. STOP

With a little more work, you can make OptionParser validate argument data for youparse strings as numbers, restrict option values to values from a list. The documentation for the OptionParser class has a much more complex example that shows off these advanced features.

See Also

  • ri OptionParser


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net