Parse command-line arguments: Difference between revisions

From Rosetta Code
Content added Content deleted
(cut off first element to really get name *after* -f argument (alternatively: increase index))
(Added zkl)
Line 605: Line 605:
Searching with -regexp allows to specify longer mnemonic names, so it still succeeds on longer flags, e.g.
Searching with -regexp allows to specify longer mnemonic names, so it still succeeds on longer flags, e.g.
$ myscript.tcl -separator '\t' ...
$ myscript.tcl -separator '\t' ...

=={{header|zkl}}==
The Argh class provides command line parsing, it can do actions during parsing, leave it for you to do after parsing, print errors, the option list, short or long options, with or without option args, etc.

File myprogram.zkl:
<lang zkl>var ip;
argh := Utils.Argh(
T("v","v","print version",fcn{println("Version stub")}),
T("n","n","ignored"),
T("z","z","zazzle"),
T("+ip","","get IP address",fcn(arg){ip=arg}),
);
parsedArgs := argh.parse(vm.arglist);

println("Unparsed stuff: ",argh.loners);
println("The IP address is ",ip);
foreach option,arg in (parsedArgs){
switch(option) {
case("z") { println("zazzle") }
}
}</lang>
<pre>zkl myprogram nc -v -n -z --ip 192.168.1.2 1-1000</pre>
{{out}}
<pre>Version stub
Unparsed stuff: L("nc","1-1000")
The IP address is 192.168.1.2
zazzle
</pre>
<pre>zkl myprogram nc -v -n -z --ip</pre>
{{out}}
<pre>
Option "ip" is missing an arg
Options:
--ip <arg>: get IP address
--n (-n) : ignored
--v (-v) : print version
--z (-z) : zazzle
</pre>

Revision as of 07:40, 8 March 2014

Parse command-line arguments is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Command-line arguments can be quite complicated, as in "nc -v -n -z -w 1 192.168.1.2 1-1000". Many languages provide a library (getopt or GetOpt) to parse the raw command line options in an intelligent way.

Bracmat

Per default, Bracmat treats all arguments as expressions and parses and evaluates them from left to right. A call to the function arg$ pops the next argument from the list of arguments and returns it as an inert string in no need of further parsing and evaluation.

bracmat arg$:?a 123 arg$:?b 77 !a+!b:?c out$!c

Output:

200

C

The man page for getopt (man 3 getopt) provides better option handling with examples. But if you just want to parse one argument... (adapted from simple database task): <lang c>#include <stdio.h> int main(int argc, char **argv){

   int i;
   const char *commands[]={"-c", "-p", "-t", "-d", "-a", NULL};
   enum {CREATE,PRINT,TITLE,DATE,AUTH};
   if (argc<2) {

usage: printf ("Usage: %s [commands]\n"

           "-c  Create new entry.\n" 
           "-p  Print the latest entry.\n" 
           "-t  Sort by title.\n" 
           "-d  Sort by date.\n" 
           "-a  Sort by author.\n",argv[0]);
       return 0;
   }
   for (i=0;commands[i]&&strcmp(argv[1],commands[i]);i++);
   switch (i) {
   case CREATE:

...

   break;
   case PRINT:

...

   break;

... ...

   default:
   printf ("Unknown command..." ...);
   goto usage;
   }
   return 0;

}</lang>

D

The getopt module in D's standard library is inspired by Perl's Getopt::Long module. The syntax of Phobos getopt infers the expected parameter types from the static types of the passed-in pointers. <lang d>import std.stdio, std.getopt;

void main(string[] args) {

   string data = "file.dat";
   int length = 24;
   bool verbose;
   enum Color { no, yes }
   Color color;
   args.getopt("length",  &length,  // Integer.
               "file",    &data,    // String.
               "verbose", &verbose, // Boolean flag.
               "color",   &color);  // Enum.
   writeln("length: ", length);
   writeln("file: ", data);
   writeln("verbose: ", verbose);
   writeln("color: ", color);

}</lang>

Usage example:
C:\getopt_test --verbose --length 12
length: 12
file: file.dat
verbose: true
color: no

Go

Most simply, implementing the suggested example from the talk page: <lang go>package main

import (

   "flag"
   "fmt"

)

func main() {

   b := flag.Bool("b", false, "just a boolean")
   s := flag.String("s", "", "any ol' string")
   n := flag.Int("n", 0, "your lucky number")
   flag.Parse()
   fmt.Println("b:", *b)
   fmt.Println("s:", *s)
   fmt.Println("n:", *n)

}</lang> Example runs:

> parse
b: false
s: 
n: 0

> parse -s bye -b
b: true
s: bye
n: 0

> parse -n 99 -s "say my name"
b: false
s: say my name
n: 99

Icon and Unicon

The Icon Programming Library provides a procedure for processing command line options. See the library reference for detailed documentation. The code below is an example.

<lang Icon>link options

procedure main(ARGLIST) /errproc := stop # special error procedure or stop() opstring := "f!s:i+r.flag!string:integer+real." # example opttable := options(ARGLIST,optstring,errproc)

if \opttable[flag] then ... # test a flag r  := opttable(real) # assign a real r2 := opttable(r) # assign another real s  := opttable(s) # assign a string i  := opttable(i) # assign an integer ... end</lang>

options.icn supports getting command-line options

J

When J starts up from the command line, the command line arguments are available in the array ARGV. On modern machines, the first command line argument is the name of the executable (the J interpeter, in this case).

Typically, the next argument (if present) is the name of a file whose contents will be executed.

Further command line analysis might include:

Test if an argument is present:
<lang j> (<'-b') e. ARGV</lang>
This is true if the argument is present and false, if it is not.
Or, find the name of an optional file:
<lang j> (ARGV i.<'-f') {:: }.ARGV,a:</lang>
This is the name of the first file named after the first -f argument, or empty if there was no such file.

Other concepts are also possible...

Mathematica

The command line is parsed and stored into a list of strings to ease manual handling by list processing functions. <lang Mathematica> $CommandLine -> {math, -v, -n, -z, -w, 1, 192.168.1.2, 1-1000} </lang>

Perl

Use the Getopt::Long module:

<lang perl># Copyright Shlomi Fish, 2013 under the MIT/X11 License.

use strict; use warnings;

use Getopt::Long qw(GetOptions); my $output_path; my $verbose = ; my $length = 24;

GetOptions(

   "length=i" => \$length,
   "output|o=s" => \$output_path,
   "verbose!" => \$verbose,

) or die ("Error in command line arguments");

print "Outputting to '", ($output_path // '(undefined)'), "' path, with ",

   ($verbose ? "Verbosity" : "No verbosity"),
   " and a length of $length.\n";

</lang>

The output from it is:

$ perl getopt-test.pl --verbose -o foo.xml
Outputting to 'foo.xml' path, with Verbosity and a length of 24.
$ perl getopt-test.pl --verbose
Outputting to '(undefined)' path, with Verbosity and a length of 24.
$ perl getopt-test.pl --verbose --length=190
Outputting to '(undefined)' path, with Verbosity and a length of 190.
$ perl getopt-test.pl --verbose --length=190 -o test.txt
Outputting to 'test.txt' path, with Verbosity and a length of 190.

Perl 6

At the end of running any top-level code (which can preprocess the arguments if it likes), Perl 6 automatically examines any remaining arguments and transforms them into a call to a MAIN routine, if one is defined. The arguments are parsed based on the signature of the routine, so that options are mapped to named arguments. <lang perl6>sub MAIN (Bool :$b, Str :$s = , Int :$n = 0, *@rest) {

   say "Bool: $b";
   say "Str: $s";
   say "Num: $n";
   say "Rest: @rest[]";

}</lang>

Output:
$ ./main -h
Usage:
  ./main [-b] [-s=<Str>] [-n=<Int>] [<rest> ...]

$ ./main -b -n=42 -s=turtles all the way down
Bool: True
Str: turtles
Num: 42
Rest: all the way down

If there are multiple MAIN subs, they are differentiated by multiple dispatch. A help message can automatically be generated for all the variants. The intent of this mechanism is not to cover every possible switch structure, but just to make it drop-dead easy to handle most of the common ones.

Python

Version 2.3+ <lang Python> from optparse import OptionParser [...] parser = OptionParser() parser.add_option("-f", "--file", dest="filename",

                 help="write report to FILE", metavar="FILE")

parser.add_option("-q", "--quiet",

                 action="store_false", dest="verbose", default=True,
                 help="don't print status messages to stdout")

(options, args) = parser.parse_args()

example:

<yourscript> --file=outfile -q </lang>

PicoLisp

PicoLisp doesn't have a library to get options. Instead, the command line is parsed at startup and handled in the following way: Each command line argument is executed (interpreted) as a Lisp source file, except that if the first character is a hypen '-', then that arguments is taken as a Lisp function call (without the surrounding parentheses). For example, the command line <lang shell>$ ./pil abc.l -foo def.l -"bar 3 4" -'mumble "hello"' -bye</lang> has the effect that

  1. The file "abc.l" is executed
  2. (foo) is called
  3. The file "def.l" is executed
  4. (bar 3 4) is called
  5. (mumble "hello") is called
  6. (bye) is called, resulting in program termination

Command line arguments like "-v", "-n" and "-z" can be implemented simply by defining three functions 'v', 'n' and 'z'.

In addition to the above mechanism, the command line can also be handled "manually", by either processing the list of arguments returned by 'argv', or by fetching arguments individually with 'opt'.

Racket

Racket has a good command-line parsing library, the following demonstrates some of its features:

<lang racket>

  1. !/usr/bin/env racket
  2. lang racket

(define loglevel 1) (define mode 'new) (define ops '()) (define root #f)

(command-line

#:multi
[("-v" "--verbose")    "more verbose"      (set! loglevel (add1 loglevel))]
[("-q" "--quiet")      "be quiet"          (set! loglevel 0)]
#:once-any
[("-i" "--in-place")   "edit in-place"     (set! mode 'in-place)]
[("-c" "--create-new") "create a new file" (set! mode 'new)]
[("-n" "--dry-run")    "do nothing"        (set! mode #f)]
#:once-each
[("-d" "--directory") dir "work in a given directory" (set! root dir)]
#:help-labels "operations to perform:"
#:multi
[("+l" "++line") "add a line"    (set! ops `(,@ops "add"))]
[("-l" "--line") "delete a line" (set! ops `(,@ops "delete"))]
[("-e" "--edit") "edit a line"   (set! ops `(,@ops "edit"))]
#:args (file . files)
(printf "Running on: ~a\n" (string-join (cons file files) ", "))
(when root (printf "In Dir:     ~a\n" root))
(printf "Mode:       ~s\n" mode)
(printf "Log level:  ~s\n" loglevel)
(printf "Operations: ~a\n" (string-join ops ", ")))

</lang>

Sample runs:

$ ./foo -h
foo [ <option> ... ] <file> [<files>] ...
 where <option> is one of
* -v, --verbose : more verbose
* -q, --quiet : be quiet
/ -i, --in-place : edit in-place
| -c, --create-new : create a new file
\ -n, --dry-run : do nothing
  -d <dir>, --directory <dir> : work in a given directory
 operations to perform:
* +l, ++line : add a line
* -l, --line : delete a line
* -e, --edit : edit a line
  --help, -h : Show this help
  -- : Do not treat any remaining argument as a switch (at this level)
 * Asterisks indicate options allowed multiple times.
 /|\ Brackets indicate mutually exclusive options.
 Multiple single-letter switches can be combined after one `-'; for
  example: `-h-' is the same as `-h --'

$ /tmp/zz.rkt -viqvvd /tmp +ll -el foo bar baz
Running on: foo, bar, baz
In Dir:     /tmp
Mode:       in-place
Log level:  2
Operations: add, add, edit, delete

REXX

<lang rexx>/*──────────────────────────────────────────────────────────────────────── The subject of parsing text (such as a command line) is ingrained in the REXX language; it has a PARSE instruction. It's too rich to describe all its functionality/capabilities here, but since the task isn't described, I'm assuming the NC [NetCat] example (or something like it) is to be parsed, and I'm guessing at its syntax (from other examples found on the web), but I'll take a incomplete stab at it.

For the most part, every command seems to have its own rules for their operands (options), as does the NC command. For instance:

       nc  -u xxx -p port1 ...
                and
       nc  -u -p port1     ...

where the -u option has an operand in the first case, but not the 2nd, even though there is something following the -u option.

I can only assume that any operand for the -u option can't start with a minus sign. ────────────────────────────────────────────────────────────────────────*/

                                      /*parse optons for a command.    */

parse arg opts /*this preserves the case of opts*/ opts=space(opts) /*this probably should be done to*/

                                      /*  get rid of extraneous blanks.*/

!.= /*assume all options to be null. */ errors=0

  do while opts\==
  parse var opts x opts
    select
    when x=='-e'   then parse var opts !.e_ opts
    when x=='-p'   then parse var opts !.p_ opts
    when x=='-n'   then !.z_=1
    when x=='-u'   then parse var opts !.uname_ !.unnn_ opts
    when x=='-ul'  then parse var opts !.ul_ opts
    when x=='-vzu' then parse var opts !.vzu_ !.vzurange opts
    when x=='-w'   then parse var opts !.wStart_ !.waddr_,
                                       !.wrange_1 '-' !.wrange_2 opts
    when x=='-z'   then !.=1
    otherwise say 'option' x "isn't a legal option."
              errors=errors+1
    end     /*select*/
 end        /*do while opts\== */

/*check for conflicts here and/or validity of values.*/

if !.z_==1 & !.n_==1 then do

                         say "error, N and Z can't both be specified."
                         errors=errors+1
                         end

if !.wrange_1\== then do /*test if whole number.*/

                       if \datatype(!.wrange1_,'W') then say ...
                       yada yada yada
                         .
                         .
                         .
                       end

/*────────────────────────────────────────────────────────────────────────

 Note: a programming trick is to append (say) an underscore to an
 option's name as to not preclude that variable being used elsewhere
 in the REXX program.   That way, the option  J  can be used, as well
 as the variable  J  in the program.

────────────────────────────────────────────────────────────────────────*/</lang>

Ruby

Ruby's standard library provides two different packages to parse command-line arguments.

  • 'getoptlong' resembles the libraries from other languages.
  • 'optparse' has more features.

Ruby with 'getoptlong'

<lang ruby>#!/usr/bin/env ruby

  1. == Synopsis
  2. pargs: Phone a friend
  3. == Usage
  4. pargs [OPTIONS]
  5. --help, -h:
  6. show usage
  7. --eddy, -e <message>
  8. call eddy
  9. --danial, -d <message>
  10. call daniel
  11. --test, -t
  12. run unit tests

require "getoptlong" require "rdoc/usage"

def phone(name, message) puts "Calling #{name}..." puts message end

def test phone("Barry", "Hi!") phone("Cindy", "Hello!") end

def main mode = :usage

name = "" message = ""

opts=GetoptLong.new( ["--help", "-h", GetoptLong::NO_ARGUMENT], ["--eddy", "-e", GetoptLong::REQUIRED_ARGUMENT], ["--daniel", "-d", GetoptLong::REQUIRED_ARGUMENT], ["--test", "-t", GetoptLong::NO_ARGUMENT] )

opts.each { |option, value| case option when "--help" RDoc::usage("Usage") when "--eddy" mode = :call name = "eddy" message = value when "--daniel" mode = :call name = "daniel" message = value when "--test" mode = :test end }

case mode when :usage RDoc::usage("Usage") when :call phone(name, message) when :test test end end

if __FILE__==$0 begin main rescue Interrupt => e nil end end</lang>

$ ./pargs.rb -h

Usage
-----
pargs [OPTIONS]

--help, -h:

   show usage

--eddy, -e <message>

   call eddy

--daniel, -d <message>

   call daniel

--test, -t

   run unit tests

$ ./pargs.rb -e Yo!
Calling eddy...
Yo!
$ ./pargs.rb --test
Calling Barry...
Hi!
Calling Cindy...
Hello!

Ruby with 'optparse'

<lang ruby>require 'optparse'

sflag = false longflag = false count = 0 percent = 50 fruit = nil

OptionParser.new do |opts|

 # Default banner is "Usage: #{opts.program_name} [options]".
 opts.banner += " [arguments...]"
 opts.separator "This demo prints the results of parsing the options."
 opts.version = "0.0.1"
 opts.on("-s", "Enable short flag") {sflag = true}
 opts.on("--long", "Enable long flag") {longflag = true}
 opts.on("-b", "--both", "Enable both -s and --long"
         ) {sflag = true; longflag = true}
 opts.on("-c", "--count", "Add 1 to count") {count += 1}
 # Argument must match a regular expression.
 opts.on("-p", "--percent PERCENT", /[0-9]+%?/i,
         "Percent [50%]") {|arg| percent = arg.to_i}
 # Argument must match a list of symbols.
 opts.on("-f", "--fruit FRUIT",
         [:apple, :banana, :orange, :pear],
         "Fruit (apple, banana, orange, pear)"
         ) {|arg| fruit = arg}
 begin
   # Parse and remove options from ARGV.
   opts.parse!
 rescue OptionParser::ParseError => error
   # Without this rescue, Ruby would print the stack trace
   # of the error. Instead, we want to show the error message,
   # suggest -h or --help, and exit 1.
   $stderr.puts error
   $stderr.puts "(-h or --help will show valid options)"
   exit 1
 end

end

print <<EOF Short flag: #{sflag} Long flag: #{longflag} Count: #{count} Percent: #{percent}% Fruit: #{fruit} Arguments: #{ARGV.inspect} EOF</lang>

$ ruby takeopts.rb -h
Usage: takeopts [options] [arguments...]
This demo prints the results of parsing the options.
    -s                               Enable short flag
        --long                       Enable long flag
    -b, --both                       Enable both -s and --long
    -c, --count                      Add 1 to count
    -p, --percent PERCENT            Percent [50%]
    -f, --fruit FRUIT                Fruit (apple, banana, orange, pear)
$ ruby takeopts.rb -v 
takeopts 0.0.1
$ ruby takeopts.rb -b -c
Short flag: true
Long flag: true
Count: 1
Percent: 50%
Fruit: 
Arguments: []
$ ruby takeopts.rb -ccccp90% -f oran -- -arg
Short flag: false
Long flag: false
Count: 4
Percent: 90%
Fruit: orange
Arguments: ["-arg"]

Tcl

The following proc detects and removes argument-less (-b) and one-argument options from the argument vector. <lang Tcl>

proc getopt {_argv name {_var ""} {default ""}} {
    upvar 1 $_argv argv $_var var
    set pos [lsearch -regexp $argv ^$name]
    if {$pos>=0} {
        set to $pos
        if {$_var ne ""} {set var [lindex $argv [incr to]]}
        set argv [lreplace $argv $pos $to]
        return 1
    } else {
        if {[llength [info level 0]] == 5} {set var $default}
        return 0
    }
}

</lang> Usage examples:

getopt argv -sep sep ";"     ;# possibly override default with user preference
set verbose [getopt argv -v] ;# boolean flag, no trailing word

Searching with -regexp allows to specify longer mnemonic names, so it still succeeds on longer flags, e.g.

$ myscript.tcl -separator '\t' ...

zkl

The Argh class provides command line parsing, it can do actions during parsing, leave it for you to do after parsing, print errors, the option list, short or long options, with or without option args, etc.

File myprogram.zkl: <lang zkl>var ip; argh := Utils.Argh( T("v","v","print version",fcn{println("Version stub")}), T("n","n","ignored"), T("z","z","zazzle"), T("+ip","","get IP address",fcn(arg){ip=arg}), ); parsedArgs := argh.parse(vm.arglist);

println("Unparsed stuff: ",argh.loners); println("The IP address is ",ip); foreach option,arg in (parsedArgs){

  switch(option)   {
     case("z") { println("zazzle") }
  }

}</lang>

zkl myprogram nc -v -n -z --ip 192.168.1.2 1-1000
Output:
Version stub
Unparsed stuff: L("nc","1-1000")
The IP address is 192.168.1.2
zazzle
zkl myprogram nc -v -n -z --ip
Output:
Option "ip" is missing an arg
Options:
  --ip <arg>: get IP address
  --n (-n) : ignored
  --v (-v) : print version
  --z (-z) : zazzle