Recipe 2.6 Parsing Command-Line Arguments


Problem

You need to parse command-line options. Java doesn't provide an API for it.

Solution

Look in the args array passed as an argument to main. Or use my GetOpt class.

Discussion

The Unix folk have had to deal with this longer than anybody, and they came up with a C-library function called getopt.[3] getopt processes your command-line arguments and looks for single-character options set off with dashes and optional arguments. For example, the command:

[3] The Unix world has several variations on getopt; mine emulates the original AT&T version fairly closely, with some frills such as long-name arguments.

sort -n -o outfile myfile1 yourfile2

runs the standard sort program. The -n tells it that the records are numeric rather than textual, and the -o outfile tells it to write its output into a file named outfile. The remaining words, myfile1 and yourfile2, are treated as the input files to be sorted. On Windows, command arguments are sometimes set off with slashes ( / ). We use the Unix form a dash in our API, but feel free to change the code to use slashes.

Each GetOpt parser is constructed to recognize a particular set of arguments; this is sensible since a given program normally has a fixed set of arguments that it accepts. You can construct an array of GetOptDesc objects that represent the allowable arguments. For the sort program shown previously, you might use:

GetOptDesc options[] = {     new GetOptDesc('n', "numeric", false},     new GetOptDesc('o', "output-file", true), }); Map optionsFound = new GetOpt(options).parseArguments(argv); if (optionsFound.get("n") != null)     sortType = NUMERIC; } else  if (optionsFound.get("o")){ ...

The simple way of using GetOpt is to call its parseArguments method.

For backward compatibility with people who learned to use the Unix version in C, the getopt( ) method can be used normally in a while loop. It returns once for each valid option found, returning the value of the character that was found or the constant DONE when all options (if any) have been processed.

Here is a complete program that uses my GetOpt class just to see if there is a -h (for help) argument on the command line:

import com.darwinsys.util.GetOpt; /** Trivial demonstration of GetOpt. If -h present, print help.  */ public class GetOptSimple {     public static void main(String[] args) {         GetOpt go = new GetOpt("h");         char c;         while ((c = go.getopt(args)) !=GetOpt.DONE) {             switch(c) {             case 'h':                 helpAndExit(0);                 break;             default:                 System.err.println("Unknown option in " +                     args[go.getOptInd( )-1]);                 helpAndExit(1);             }         }         System.out.println( );     }     /** Stub for providing help on usage      * You can write a longer help than this, certainly.      */     static void helpAndExit(int returnValue) {         System.err.println("This would tell you how to use this program");         System.exit(returnValue);     } }

This longer demo program has several options:

import com.darwinsys.lang.GetOpt; import com.darwinsys.lang.GetOptDesc; import java.util.*; /** Demonstrate the modern way of using GetOpt. This allows a subset of  * <pre>Unix sort options: sort -n -o outfile infile1 infile2</pre>  * which means: sort numerically (-n), writing to file "outfile" (-o  * outfile), sort from infile1 and infile2.  */ public class GetOptDemoNew {     public static void main(String[] argv) {         boolean numeric_option = false;         boolean errs = false;         String outputFileName = null;         GetOptDesc options[] = {             new GetOptDesc('n', "numeric", false),             new GetOptDesc('o', "output-file", true),         };         GetOpt parser = new GetOpt(options);         Map optionsFound = parser.parseArguments(argv);         Iterator it = optionsFound.keySet( ).iterator( );         while (it.hasNext( )) {             String key = (String)it.next( );             char c = key.charAt(0);             switch (c) {                 case 'n':                     numeric_option = true;                     break;                 case 'o':                     outputFileName = (String)optionsFound.get(key);                     break;                 case '?':                     errs = true;                     break;                 default:                     throw new IllegalStateException(                     "Unexpected option character: " + c);             }         }         if (errs) {             System.err.println("Usage: GetOptDemo [-n][-o file][file...]");         }         System.out.print("Options: ");         System.out.print("Numeric: " + numeric_option + ' ');         System.out.print("Output: " + outputFileName + "; ");         System.out.print("Inputs: ");         List files = parser.getFilenameList( );         for (int i = 0; i < files.size( ); i++) {             System.out.print(files.get(i));             System.out.print(' ');         }         System.out.println( );     } }}

If we invoke it several times with different options, including both single-argument and long-name options, here's how it behaves:

> java GetOptDemoNew Options: Numeric: false Output: null; Inputs:  > java GetOptDemoNew -M Options: Numeric: false Output: null; Inputs: -M  > java GetOptDemoNew -n a b c Options: Numeric: true Output: null; Inputs: a b c  > java GetOptDemoNew -numeric a b c Options: Numeric: true Output: null; Inputs: a b c  > java GetOptDemoNew -numeric -output-file /tmp/foo a b c Options: Numeric: true Output: /tmp/foo; Inputs: a b c

A longer example exercising all the ins and outs of this version of GetOpt can be found in the online source under darwinsys/src/regress. The source code for GetOpt itself is shown in Example 2-2.

Example 2-2. Source code for GetOpt
package com.darwinsys.lang; import com.darwinsys.util.Debug; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Iterator; /** A class to implement Unix-style (single-character) command-line argument  * parsing. Originally patterned after (but not using code from) the Unix   * getopt(3) program, this has been redesigned to be more Java-friendly.  * <p>  * This is <em>not</em> threadsafe; it is expected to be used only from main( ).  * <p>  * For another way of dealing with command lines, see the  * <a href="http://jakarta.apache.org/commons/cli/">Jakarta Commons  * Command Line Interface</a>.  * @author Ian F. Darwin, ian@darwinsys.com  * @version $dollar;Id: ch02, v1.4 2004/05/04 20:11:12 ian Exp $  */ public class GetOpt {     /** The list of file names found after args */     protected List fileNameArguments;     /** The set of characters to look for */     protected GetOptDesc[] options;     /** Where we are in the options */     protected int optind = 0;     /** Public constant for "no more options" */     public static final int DONE = 0;     /** Internal flag - whether we are done all the options */     protected boolean done = false;     /** The current option argument. */     protected String optarg;     /** Retrieve the current option argument */     public String optarg( ) {         return optarg;     }     /* Construct a GetOpt parser, given the option specifications      * in an array of GetOptDesc objects. This is the preferred constructor.      */     public GetOpt(GetOptDesc[] options) {         this.options = options;     }     /* Construct a GetOpt parser, storing the set of option characters.      * This is a legacy constructor for backward compatibility.      */     public GetOpt(String patt) {         // Pass One: just count the letters         int n = 0;         for (int i = 0; i<patt.length( ); i++) {             if (patt.charAt(i) != ':')                 ++n;         }         if (n == 0)             throw new IllegalArgumentException(                 "No option letters found in " + patt);         // Pass Two: construct an array of GetOptDesc objects.         options = new GetOptDesc[n];         for (int i = 0, ix = 0; i<patt.length( ); i++) {             char c = patt.charAt(i);             boolean argTakesValue = false;             if (i < patt.length( ) - 1 && patt.charAt(i+1) == ':') {                 argTakesValue = true;                 ++i;             }             options[ix++] = new GetOptDesc(c, null, argTakesValue);             Debug.println("getopt",                 "CONSTR: options[" + ix + "] = " + c + ", " + argTakesValue);         }     }     /** Reset this GetOpt parser */     public void rewind( ) {         fileNameArguments = null;         done = false;         optind = 0;     }     /** Array used to convert a char to a String */     private static char[] strConvArray = { 0 };     /**       * Modern way of using GetOpt: call this once and get all options.      * <p>      * This parses the options and returns a Map whose keys are the found options.      * Normally followed by a call to getFilenameList( ).      * @return a Map whose keys are Strings of length 1 (containing the char      * from the option that was matched) and whose value is a String      * containing the value, or null for a non-option argument.      */     public Map parseArguments(String[] argv) {         Map optionsAndValues = new HashMap( );         fileNameArguments = new ArrayList( );         for (int i = 0; i < argv.length; i++) {             Debug.println("getopt", "parseArg: i=" + i + ": arg " + argv[i]);             char c = getopt(argv);             if (c != DONE) {                 strConvArray[0] = c;                 optionsAndValues.put(new String(strConvArray), optarg);                 // If this arg takes an option, we must skip it here.                 if (optarg != null)                     ++i;             } else {                 fileNameArguments.add(argv[i]);             }         }         return optionsAndValues;     }     /** Get the list of filename-like arguments after options */     public List getFilenameList( ) {         if (fileNameArguments == null) {             throw new IllegalArgumentException(                 "Illegal call to getFilenameList( ) before parseOptions( )");         }         return fileNameArguments;     }     /** The true heart of getopt, whether used old way or new way:      * returns one argument; call repeatedly until it returns DONE.      */     public char getopt(String argv[]) {         Debug.println("getopt",             "optind=" + optind + ", argv.length="+argv.length);         if (optind == (argv.length)-1) {             done = true;         }         // If we are (now) finished, bail.         if (done) {             return DONE;         }         // TODO - two-pass, 1st check long args, 2nd check for         // char, may be multi char as in "-no outfile" == "-n -o outfile".         // Pick off the next command-line argument; check if it starts "-".         // If so, look it up in the list.         String thisArg = argv[optind++];         if (thisArg.startsWith("-")) {             optarg = null;             for (int i=0; i<options.length; i++) {                 if ( options[i].argLetter == thisArg.charAt(1) ||                     (options[i].argName != null &&                      options[i].argName == thisArg.substring(1))) { // found it                     // If it needs an option argument, get it.                     if (options[i].takesArgument) {                         if (optind < argv.length) {                             optarg = argv[optind];                              ++optind;                         } else {                             throw new IllegalArgumentException(                                 "Option " + options[i].argLetter +                                 " needs value but found end of arg list");                         }                     }                     return options[i].argLetter;                 }             }             // Began with "-" but not matched, so must be error.             return '?';         } else {             // Found non-argument non-option word in argv: end of options.             done = true;             return DONE;         }     }     /** Return optind, the index into args of the last option we looked at */     public int getOptInd( ) {         return optind;     } }

See Also

GetOpt is an adequate tool for processing command-line options. You may come up with something better and contribute it to the Java world; this is left as an exercise for the reader.

For another way of dealing with command lines, see the Jakarta Commons Command Line Interface, which can be found at http://jakarta.apache.org/commons/cli/.



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

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