Section 14.3. The Shell Library

14.2. Command-Line Options and Arguments

Rumors of the death of the command line are greatly exaggerated. Although we live in the age of the GUI, every day thousands of us retreat to the older text-based interfaces for one reason or another.

Ruby has many of its roots in UNIX, as we've said. Yet even in the Windows world, there is such a thing as a command line, and, frankly, we don't see it going away any time soon.

When operating at this level, parameters and switches are used to communicate with the program at the time of its invocation. This section shows how to deal with these parameters (or arguments) and switches (or options).

14.2.1. Parsing Command-Line Options

The getoptlong library is probably the most commonly used command-line parser. (The getopts.rb library is considered obsolete because it has more limited functionality.) It can accept both single-letter and longer option names, and it recognizes the double hyphen (--) as meaning the end of all the options. Its behavior is essentially the same as its GNU counterpart.

The GetoptLong class must be instantiated, giving a parser object. This object can then be set up with the allowed command-line options and used to retrieve them one at a time.

The parser object has a set_options method that takes a list of arrays. Each array contains one or more options (as strings) and one "argument flag," which tells whether an argument is allowed for that option. The options in each array are considered synonyms; the first one mentioned is the "canonical name" of the option, as returned by a get operation.

As an example, suppose that we have a tool with these options: -h or --help will print help information; -f or --file will specify a filename argument; and -l or --lines will truncate the output after the specified number of lines (defaulting to 100).

We could begin in this way:

require "getoptlong" parser = parser.set_options(          ["-h", "--help", GetoptLong::NO_ARGUMENT],          ["-f", "--file", GetoptLong::REQUIRED_ARGUMENT],          ["-l", "--lines", GetoptLong::OPTIONAL_ARGUMENT])

Now we can use a loop to call get repeatedly (see Listing 14.1); we can fake a post-test loop because we are using begin and end anyway.) A synonym for get is get_option; there are also iterators named each and each_option, which are identical.

Listing 14.1. Getting Command-Line Options

filename = nil lines = 0               # Default means no truncating loop do   begin     opt, arg = parser.get     break if not opt     # Only for debugging purposes...     puts (opt + " => " + arg)     case opt       when "-h"         puts "Usage: ..."         break           # Stop processing if -h       when "-f"         filename = arg  # Save the file argument       when "-l"         if arg != ""           lines = arg   # Save lines arg (if given)         else           lines = 100   # Default for truncating         end     end   rescue => err     puts err     break   end end puts "filename = #{filename}" puts "lines    = #{lines}"

Note that get returns nil for a nonexistent option but a null string for a nonexistent argument. This may be a bug.

Note also that we are catching errors here. Four possible exceptions could be raised, as summarized here:

  • AmbiguousOption A long option name seems to have been abbreviated, but it isn't unique.

  • InvalidOption The option is unknown.

  • MissingArgument The option is missing its argument.

  • NeedlessArgument The option has an argument when it isn't expected to take an argument.

Errors are normally reported to stderr when they occur, but the quiet= accessor can be set to TRue to override this.

There are other features of getoptlong, which we haven't discussed here. See the documentation for further details.

There are also other possibilities, such as OptionParser, that offer somewhat different functionality and usage. Refer to the Ruby Application Archive for more information.

14.2.2. Working with ARGF

The special global constant ARGF represents the pseudo-file, resulting from a concatenation of every file named on the command line. It behaves like an IO object in most ways.

When you have a "bare" input method (without a receiver), you are typically using a method mixed in from the Kernel module. (Examples are gets and readlines.) The actual source of input will default to STDIN if no files are on the command line. If there are files, however, input will be taken from them. End of file will of course be reached only at the end of the last file.

If you prefer, you can access ARGF explicitly using the following fragment:

# Copy all files to stdout puts ARGF.readlines

Perhaps contrary to most people's expectations, end of file is set after each file. The previous code fragment will output all the files. This one will output only the first:

until ARGF.eof?   puts ARGF.gets end

Whether this is a bug or a feature, we will leave to you to decide. Of course, other unexpected surprises might actually be pleasant. The input isn't simply a stream of bytes flowing through our program; we can actually perform operations such as seek and rewind on ARGF as though it were a "real file."

There is a file method associated with ARGF; it returns an IO object corresponding to the file currently being processed. As such, the value it returns will change as the files on the command line are processed in sequence.

What if we don't want command-line arguments to be interpreted as files? The solution is to not use the "bare" (receiverless) call of the input methods. If you want to read standard input, you can use STDIN as the receiver, and all will work as expected.

14.2.3. Working with ARGV

The global constant ARGV represents the list of arguments passed to the Ruby program via the command line. This is essentially an array.

n = ARGV.size argstr = '"' + ARGV*"," + '"' puts "I was given #{n} arguments..." puts "They are: #{argstr}" puts "Note that ARGV[0] = #{ARGV[0]}"

Assume that we invoke this little program with the arguments red green blue on the command line. It then produces this output:

I was given 3 arguments. They are: "red,green,blue" Note that ARGV[0] = red

Obviously there is no need for an argument count as in the old days; that information is part of the array.

Another thing that might trip up oldtimers is the assignment of the zeroth argument to an actual argument (rather than, for example, the script name). The arguments themselves are zero-based rather than one-based as in C and the various shell languages.

The Ruby Way(c) Solutions and Techniques in Ruby Programming
The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition)
ISBN: 0672328844
EAN: 2147483647
Year: 2004
Pages: 269
Authors: Hal Fulton

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: