Codebase list erlang-getopt / fresh-snapshots/main
fresh-snapshots/main

Tree @fresh-snapshots/main (Download .tar.gz)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
Getopt for Erlang
=================

Command-line parsing module that uses a syntax similar to that of GNU *getopt*.


Requirements
------------

You should only need a somewhat recent version of Erlang/OTP. The module has
been tested with all versions of Erlang starting with R13B and ending with 20.

You also need a recent version of [rebar3](http://www.rebar3.org/) in
the system path.

Installation
------------

To compile the module you simply run `rebar3 compile`.

To run the unit tests run `rebar3 eunit`.

To build the (very) limited documentation run `rebar edoc`.

To use getopt in your project you can just add it as a dependency in your
`rebar.config` file in the following way:
```erlang
{deps,
 [
  {getopt, "1.0.2"}
 ]
}
```


Usage
-----

The `getopt` module provides four functions:

```erlang
parse([{Name, Short, Long, ArgSpec, Help}], Args :: string() | [string()]) ->
    {ok, {Options, NonOptionArgs}} | {error, {Reason, Data}}

tokenize(CmdLine :: string()) -> [string()]

usage([{Name, Short, Long, ArgSpec, Help}], ProgramName :: string()) -> ok

usage([{Name, Short, Long, ArgSpec, Help}], ProgramName :: string(),
      CmdLineTail :: string()) -> ok

usage([{Name, Short, Long, ArgSpec, Help}], ProgramName :: string(),
      CmdLineTail :: string(), OptionsTail :: [{string(), string}]) -> ok
```

The `parse/2` function receives a list of tuples with the command line option
specifications. The type specification for the tuple is:

```erlang
-type arg_type() :: 'atom' | 'binary' | 'utf8_binary' | 'boolean' | 'float' | 'integer' | 'string'.

-type arg_value() :: atom() | binary() | boolean() | float() | integer() | string().

-type arg_spec() :: arg_type() | {arg_type(), arg_value()} | undefined.

-type option_spec() :: {
                   Name    :: atom(),
                   Short   :: char() | undefined,
                   Long    :: string() | undefined,
                   ArgSpec :: arg_spec(),
                   Help    :: string() | undefined
                  }.
```

The elements of the tuple are:

  - `Name`: name of the option.
  - `Short`: character for the short option (e.g. $i for -i).
  - `Long`: string for the long option (e.g. "info" for --info).
  - `ArgSpec`: data type and optional default value the argument will be converted to.
  - `Help`: help message that is shown for the option when `usage/2` is called.

e.g.

```erlang
{port, $p, "port", {integer, 5432}, "Database server port"}
```

The second parameter receives the list of arguments as passed to the `main/1`
function in escripts or the unparsed command line as a string.

If the function is successful parsing the command line arguments it will return
a tuple containing the parsed options and the non-option arguments. The options
will be represented by a list of key-value pairs with the `Name` of the
option as *key* and the argument from the command line as *value*. If the option
doesn't have an argument, only the atom corresponding to its `Name` will be
added to the list of options. For the example given above we could get something
like `{port, 5432}`. The non-option arguments are just a list of strings with
all the arguments that did not have corresponding options.

e.g. Given the following option specifications:

```erlang
OptSpecList =
    [
     {host,    $h,        "host",    {string, "localhost"}, "Database server host"},
     {port,    $p,        "port",    integer,               "Database server port"},
     {dbname,  undefined, "dbname",  {string, "users"},     "Database name"},
     {xml,     $x,        undefined, undefined,             "Output data in XML"},
     {verbose, $v,        "verbose", integer,               "Verbosity level"},
     {file,    undefined, undefined, string,                "Output file"}
    ].
```

And this command line:

```erlang
Args = "-h myhost --port=1000 -x myfile.txt -vvv dummy1 dummy2"
```

Which could also be passed in the format the `main/1` function receives the arguments in escripts:

```erlang
Args = ["-h", "myhost", "--port=1000", "-x", "file.txt", "-vvv", "dummy1", "dummy2"].
```

The call to `getopt:parse/2`:

```erlang
getopt:parse(OptSpecList, Args).
```

Will return:

```erlang
{ok,{[{host,"myhost"},
      {port,1000},
      xml,
      {file,"file.txt"},
      {dbname,"users"},
      {verbose,3}],
     ["dummy1","dummy2"]}}
```

The `tokenize/1` function will separate a command line string into
tokens, taking into account whether an argument is single or double
quoted, a character is escaped or if there are environment variables to
be expanded. e.g.:

```erlang
getopt:tokenize("  --name John\\ Smith --path \"John's Files\" -u ${USER}").
```

Will return something like:

```erlang
["--name","John Smith","--path","John's Files","-u","jsmith"]
```

The other functions exported by the `getopt` module (`usage/2`, `usage/3`
and `usage/4`) are used to show the command line syntax for the program.
For example, given the above-mentioned option specifications, the call to
`getopt:usage/2`:

```erlang
getopt:usage(OptSpecList, "ex1").
```

Will show (on *standard_error*):

    Usage: ex1 [-h <host>] [-p <port>] [--dbname <dbname>] [-x] [-v] <file>

      -h, --host                    Database server host
      -p, --port                    Database server port
      --dbname                      Database name
      -x                            Output data in XML
      -v                            Verbosity level
      <file>                        Output file

This call to `getopt:usage/3` will add a string after the usage command line:

```erlang
getopt:usage(OptSpecList, "ex1", "[var=value ...] [command ...]").
```

Will show (on *standard_error*):

    Usage: ex1 [-h <host>] [-p <port>] [--dbname <dbname>] [-x] [-v <verbose>] <file> [var=value ...] [command ...]

      -h, --host            Database server host
      -p, --port            Database server port
      --dbname              Database name
      -x                    Output data in XML
      -v, --verbose         Verbosity level
      <file>                Output file

Whereas this call to `getopt:usage/3` will also add some lines to the options
help text:

```erlang
getopt:usage(OptSpecList, "ex1", "[var=value ...] [command ...]",
             [{"var=value", "Variables that will affect the execution (e.g. debug=1)"},
              {"command",   "Commands that will be executed (e.g. count)"}]).
```

Will show (on *standard_error*):

    Usage: ex1 [-h <host>] [-p <port>] [--dbname <dbname>] [-x] [-v <verbose>] <file> [var=value ...] [command ...]

      -h, --host            Database server host
      -p, --port            Database server port
      --dbname              Database name
      -x                    Output data in XML
      -v, --verbose         Verbosity level
      <file>                Output file
      var=value             Variables that will affect the execution (e.g. debug=1)
      command               Commands that will be executed (e.g. count)


Command-line Syntax
-------------------

The syntax supported by the `getopt` module is very similar to that followed
by GNU programs, which is described [here](http://www.gnu.org/s/libc/manual/html_node/Argument-Syntax.html).

Options can have both short (single character) and long (string) option names.

A short option can have the following syntax:

    -a         Single option 'a', no argument or implicit boolean argument
    -a foo     Single option 'a', argument "foo"
    -afoo      Single option 'a', argument "foo"
    -abc       Multiple options: 'a'; 'b'; 'c'
    -bcafoo    Multiple options: 'b'; 'c'; 'a' with argument "foo"
    -aaa       Multiple repetitions of option 'a'

A long option can have the following syntax:

    --foo      Single option 'foo', no argument
    --foo=bar  Single option 'foo', argument "bar"
    --foo bar  Single option 'foo', argument "bar"


Argument Types
--------------

The arguments allowed for options are: *atom*; *binary*; *utf8_binary*; *boolean*;
*float*; *integer*; *string*.
The `getopt` module checks every argument to see if it can be converted to its
correct type.

In the case of boolean arguments, the following values (in lower or
upper case) are considered `true`: *true*; *t*; *yes*; *y*; *on*; *enabled*; *1*.
These ones are considered `false`: *false*; *f*; *no*; *n*; *off*; *disabled*; *0*.

Numeric arguments can only be negative when passed as part of an assignment expression.

e.g. `--increment=-100` is a valid expression; whereas `--increment -100` is invalid

Arguments of `utf8_binary` type allow proper binary encoding of arguments containing
code points greater than 255. The resulting value is a normalized UTF-8 binary.

As of Erlang/20, `standard_error` device has `unicode` option set to `false`.
It prevents correct printing of usage for arguments containing unicode
binaries/strings as default values. To fix this, one needs to enable unicode:

```erlang
io:setopts(standard_error, [{unicode, true}]).
```


Implicit Arguments
------------------

The arguments for options with the *boolean* and *integer* data types can sometimes
be omitted. In those cases the value assigned to the option is *true* for *boolean*
arguments and *1* for integer arguments.


Multiple Repetitions
--------------------

An option can be repeated several times, in which case there will be multiple
appearances of the option in the resulting list. The only exceptions are short
options with integer arguments. In that particular case, each appearance of
the short option within a single command line argument will increment the
number that will be returned for that specific option.

e.g. Given an option specification list with the following format:

```erlang
OptSpecList =
    [
     {define,  $D, "define",  string,  "Define a variable"},
     {verbose, $v, "verbose", integer, "Verbosity level"}
    ].
```

The following invocation:

```erlang
getopt:parse(OptSpecList, "-DFOO -DVAR1=VAL1 -DBAR --verbose --verbose=3 -v -vvvv dummy").
```

would return:

```erlang
{ok,{[{define,"FOO"}, {define,"VAR1=VAL1"}, {define,"BAR"},
      {verbose,1}, {verbose,3}, {verbose,1}, {verbose,4}],
     ["dummy"]}}
```


Positional Options
------------------

We can also have options with neither short nor long option names. In this case,
the options will be taken according to their position in the option specification
list passed to `getopt:/parse2`.

For example, with the following option specifications:

```erlang
OptSpecList =
    [
     {xml,         $x,        "xml",     undefined, "Output data as XML"},
     {dbname,      undefined, undefined, string,    "Database name"},
     {output_file, undefined, undefined, string,    "File where the data will be saved to"}
    ].
```

This call to `getopt:parse/2`:

```erlang
getopt:parse(OptSpecList, "-x mydb file.out dummy dummy").
```

Will return:

```erlang
{ok,{[xml,{dbname,"mydb"},{output_file,"file.out"}],
     ["dummy","dummy"]}}
```


Option Terminators
------------------

The string `--` is considered an option terminator. This means that all the
command-line arguments after it are considered non-option arguments and will be
returned without being evaluated even if they follow the *getopt* syntax.

e.g. This invocation using the first option specification list in the document:

```erlang
getopt:parse(OptSpecList, "-h myhost -p 1000 -- --dbname mydb dummy").
```

will return:

```erlang
{ok,{[{host,"myhost"}, {port,1000},{dbname,"users"}],
     ["--dbname","mydb","dummy"]}}
```

Notice that the *dbname* option was assigned the value `users` instead of `mydb`.
This happens because the option terminator prevented *getopt* from evaluating it
and the default value was assigned to it.


Non-option Arguments
--------------------

The single `-` character is always considered as a non-option argument.

e.g. This invocation using the specification list from the previous example:

```erlang
getopt:parse(OptSpecList, "-h myhost -p 1000 - --dbname mydb dummy").
```

will return:

```erlang
{ok,{[{host,"myhost"}, {port,1000}, {dbname,"mydb"}],
     ["-","dummy"]}}
```


Arguments with embedded whitespace
----------------------------------

Arguments that have embedded whitespace have to be quoted with either
single or double quotes to be considered as a single
argument.


e.g. Given an option specification list with the following format:

```erlang
OptSpecList =
    [
     {define,  $D, "define",  string,  "Define a variable"},
     {user,    $u, "user",    string,  "User name"}
    ].
```

The following invocation:

```erlang
getopt:parse(OptSpecList,
             "-D'FOO=VAR 123' --define \"VAR WITH SPACES\" -u\"my user name\"").
```

would return:

```erlang
{ok,{[{define,"FOO=VAR 123"},
      {define,"VAR WITH SPACES"},
      {user,"my user name"}],
     []}}
```

When parsing a command line with unclosed quotes the last argument
will be a single string starting at the position where the last quote
was entered.

e.g. The following invocation:

```erlang
getopt:parse(OptSpecList, "--user ' my user ' \"argument with unclosed quotes").
```

would return:

```erlang
{ok,{[{user," my user "}],
     ["argument with unclosed quotes"]}}
```


Environment variable expansion
------------------------------

`getopt:parse/2` will expand environment variables when used with a command
line that is passed as a single string. The formats that are supported
for environment variable expansion are:

  - $VAR (simple Unix/bash format)
  - ${VAR} (full Unix/bash format)
  - %VAR% (Windows format)

If a variable is not present in the environment it will not be
expanded. Variables can be expanded within double-quoted and free
arguments. *getopt* will not expand environment variables within
single-quoted arguments.

e.g. Given the following option specification list:

```erlang
OptSpecList =
    [
     {path,    $p, "path",    string,  "File path"}
    ].
```

The following invocation:

```erlang
getopt:parse(OptSpecList, "--path ${PATH} $NONEXISTENT_DUMMY_VAR").
```

would return (depending on the value of your PATH variable) something like:

```erlang
{ok,{[{path, "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}],
     ["$NONEXISTENT_DUMMY_VAR"]}}
```

Currently, *getopt* does not perform wildcard expansion of file paths.


Escaping arguments
==================

Any character can be escaped by prepending the \ (backslash) character
to it.

e.g.

```erlang
getopt:parse(OptSpecList, "--path /john\\'s\\ files dummy").
```

Will return:

```erlang
{ok,{[{path,"/john's files"}],["dummy"]}}
```