├── AUTHORS ├── LICENSE ├── README.md └── functions └── argu.fish /AUTHORS: -------------------------------------------------------------------------------- 1 | # This file lists all individuals having contributed content to the repository. 2 | # The contents below are generated as follows: 3 | # git log --format='%aN <%aE>' | env LC_ALL=C.UTF-8 sort -uf 4 | 5 | Stephen Coakley 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Stephen Coakley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `argu`: ARGument Understander 2 | Sane and easy to use argument parser for [Oh My Fish][omf]. 3 | 4 | [![MIT License](https://img.shields.io/badge/license-MIT-007EC7.svg?style=flat-square)](LICENSE) 5 | 6 | > Tired of arguing over arguments? Fear not! The ARGument Understander is here to help you understand clearly the arguments you are given and live a life of happiness. 7 | 8 | Focus on writing your function, not on argument parsing. `argu` does the hard work of parsing function arguments for you in a standard way. 9 | 10 | 11 | ## Usage 12 | 13 | argu ... -- [ARGV...] 14 | 15 | `argu` parses a series of arguments by matching each one against a list of option definitions. A definition is simply a special string that describes how that argument is used. For example, if we had an `--awesome` flag, we could match it like this: 16 | 17 | ```fish 18 | argu awesome -- $argv 19 | ``` 20 | 21 | If `$argv` contained the `--awesome` flag, it would be printed out appropriately. If our utility has multiple flags and options, we can tell `argu` about all of them and have them parsed: 22 | 23 | ```fish 24 | argu a awesome b: boring: -- $argv 25 | ``` 26 | 27 | This defines four different options. First, we have an `-a` short flag, and a long `--awesome` flag. These are simply boolean in nature and either are present or not. We also define `b:` and `boring:`; the colon (`:`) indicates that these are options that have _values_, which accept a string associated with the option when passed. This will match something like `--boring tutorial` on the command line, where the string `tutorial` is associated with the `boring` option. 28 | 29 | When defined this way, our options require a value. If a value is not given, such as passing in `-b` by itself, we get an error message. If we want our option values themselves to be optional, add another colon to the end of the definition: 30 | 31 | ```fish 32 | argu a awesome b:: boring:: -- $argv 33 | ``` 34 | 35 | With this definition, it is OK to use `-b` or `--boring` by themselves or with an associated value. 36 | 37 | Now to use our definitions, we simply place the arguments we want to parse (usually `$argv`) after the `--` argument: 38 | 39 | ```fish 40 | $ argu a awesome b: boring: -- --awesome -b tutorial 41 | --awesome 42 | -b tutorial 43 | ``` 44 | 45 | If we try to put some flags into `$argv` that don't match any of our definitions, `argu` will exit and display a message for us: 46 | 47 | ```fish 48 | $ argu a awesome b: boring: -- --verbose 49 | Unknown option `--verbose'. 50 | ``` 51 | 52 | If you simply want to pass in some extra values that aren't associated with an option, you can do that too. Every such value will be associated with the underscore (`_`) special option: 53 | 54 | ```fish 55 | $ argu a awesome b: boring: -- --awesome -b tutorial extra values 56 | --awesome 57 | -b tutorial 58 | _ extra 59 | _ values 60 | ``` 61 | 62 | When called, `argu` will parse the given arguments and print out each matching argument name and value. This output can be easily combined with `read` to begin acting on these values: 63 | 64 | ```fish 65 | $ argu a awesome b: boring: -- --awesome -b tutorial | while read -l opt value 66 | echo "Option: $opt" 67 | echo "Value: $value" 68 | end 69 | 70 | Option: --awesome 71 | Value: 72 | Option: -b 73 | Value: tutorial 74 | ``` 75 | 76 | 77 | ## Examples 78 | An actual code example speaks a thousand words, so here's an example on how `argu` can be used to parse function arguments: 79 | 80 | ```fish 81 | function my_utility 82 | argu l long x: o:: optional:: -- $argv | while read -l opt value 83 | switch $opt 84 | case -l --long 85 | echo handle `-l --long` 86 | case -x 87 | echo handle `-x` w/ argument `$value` 88 | case -o --optional 89 | echo handle `-o --optional` w/ optional argument `$value` 90 | case _ 91 | echo operand: `$value` 92 | end 93 | end 94 | end 95 | ``` 96 | 97 | 98 | ## Syntax 99 | `argu` obtains options and their arguments from a list of parameters that, as indicated by each ``, are single letters preceded by a `-` or words preceded by `--` and possibly followed by an argument value. Each definition has the following grammar: 100 | 101 | ::= | 102 | ::= "" | ":" | "::" 103 | 104 | A definition beginning with `` defines a short option, while a definition beginning with `` defines a long option. A short option will match `-`, while a long option will match `--`. 105 | 106 | If a `` or `` is followed by a `:`, the option is expected to have an argument, which may be supplied separately or next to the option without spaces in the same string. To indicate optional arguments, use an additional `:` character after a `:` at the end of the definition. 107 | 108 | Both required and optional values for arguments can be supplied either in the same string as the option, or in the string following the option. For short options, the value can be appended without spaces, e.g, `-value`. For long options, use a `=` character after the option, e.g, `--=value`. 109 | 110 | In general, `argu` parses arguments according to [The Open Group Utility Syntax Guidelines][utilconv], except that it is less restrictive. The following is a summary of the features: 111 | 112 | - Short options; single letters preceded by `-`, and long options; words preceded by `--`, are both supported. 113 | 114 | - Single letters may be grouped. `-abc` → `-a -b -c` 115 | 116 | - Options required to take an argument can specify the argument either in the same string as the option or separated from the by a space. (1) `-a argument`, (2) `-aargument` 117 | 118 | - Options that can take an argument optionally shall specify the argument in the same string as the option argument if in short option style: `-aargument`, or separated by a `=` if in long form: `--long-form=argument`. If a blank space is used, the following argument will be treated independently. 119 | 120 | - Options can appear multiple times in the same argument list. `argu` will print every match sequentially on each call, and should default to the short form of the option if available. 121 | 122 | 123 | ## Relation to getopts 124 | `argu` has been written from scratch with a new algorithm, to improve performance and provide more consistent parsing. It aims to replace [plugin-getopts], a separate plugin. Inspiration is drawn from this plugin, but the usage and implementation are **not** related nor compatible. See the `README` on the above plugin to compare the changes if you are migrating to `argu` from [plugin-getopts]. 125 | 126 | 127 | ## Inspiration and related links 128 | - [The Open Group Base Specifications: Utility Conventions][utilconv] 129 | - [GNU getopt](http://man7.org/linux/man-pages/man1/getopt.1.html) 130 | - [zparseopts](http://linux.die.net/man/1/zshmodules) 131 | - Credit goes to https://github.com/fishery/getopts for the `while read` idea for reading options. 132 | 133 | 134 | ## License 135 | [MIT][mit] © [Stephen Coakley][author]. See the [AUTHORS](AUTHORS) file for a generated list of all contributors, and the [LICENSE](LICENSE) file for license details. 136 | 137 | 138 | [author]: https://github.com/coderstephen 139 | [getopts]: http://en.wikipedia.org/wiki/Getopts 140 | [mit]: http://opensource.org/licenses/MIT 141 | [omf]: https://www.github.com/oh-my-fish 142 | [plugin-getopts]: https://github.com/oh-my-fish/plugin-getopts 143 | [utilconv]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02 144 | -------------------------------------------------------------------------------- /functions/argu.fish: -------------------------------------------------------------------------------- 1 | function argu -d "Parse command options" 2 | # Iterate over each input argument to parse the option configuration. 3 | for arg in $argv 4 | set -e argv[1] 5 | switch "$arg" 6 | # We've reached the end of the option definitions. 7 | case -- 8 | break 9 | # Short option. 10 | case '?::' '?:' '?' 11 | set options $options -$arg 12 | # Long option. 13 | case '*' 14 | set options $options --$arg 15 | end 16 | end 17 | 18 | # Now that we've parsed the option definitions, we can parse the given options 19 | # themselves. 20 | while set -q argv[1] 21 | switch "$argv[1]" 22 | # End all option parsing; everything else should be treated as values. 23 | case -- 24 | set -e argv[1] 25 | break 26 | 27 | # Match a long option with a value. 28 | case '--*=*' 29 | set -l parts (echo $argv[1] | tr = '\n') 30 | 31 | if contains -- $parts[1] $options 32 | echo "Flag `$parts[1]' does not accept a value." >&2 33 | return 1 34 | end 35 | 36 | contains -- $parts[1]: $options 37 | or contains -- $parts[1]:: $options 38 | and echo -E "$parts" 39 | or echo "Unknown option `$parts[1]'." >&2 40 | 41 | # Match a short or long single option. 42 | case '--*' '-?' 43 | if contains -- $argv[1] $options 44 | echo -E "$argv[1]" 45 | else if contains -- $argv[1]: $options 46 | # Option must have a value. 47 | if begin; not set -q argv[2]; or expr "$argv[2]" : '-.*' > /dev/null; end 48 | echo "Option `$argv[1]' requires a value." >&2 49 | return 1 50 | end 51 | 52 | echo -E "$argv[1..2]" 53 | set -e argv[2] 54 | else if contains -- $argv[1]:: $options 55 | # Value is not required. 56 | if begin; not set -q argv[2]; or expr "$argv[2]" : '-.*' > /dev/null; end 57 | echo -E "$argv[1]" 58 | else 59 | echo -E "$argv[1..2]" 60 | set -e argv[2] 61 | end 62 | else 63 | echo "Unknown option `$argv[1]'." >&2 64 | end 65 | 66 | # Match a short option with a value, or a series of flags. 67 | case '-??*' 68 | set -l flag (echo $argv[1] | cut -c -2) 69 | 70 | # If first flag does not take a value, we assume all following characters 71 | # are also flags. 72 | if contains -- $flag $options 73 | echo -E "$flag" 74 | # Parse combined flags as well. 75 | set -l flags (echo $argv[1] | cut -c 3- | fold -w1) 76 | for flag in $flags 77 | if begin; contains -- -$flag $options; or contains -- -$flag:: $options; end 78 | echo -E "-$flag" 79 | else if contains -- -$flag: $options 80 | echo "Option `-$flag' requires a value." >&2 81 | return 1 82 | else 83 | echo "Unknown option `-$flag'." >&2 84 | end 85 | end 86 | else 87 | # We must have a single short option with a value. 88 | set -l value (echo $argv[1] | cut -c 3-) 89 | contains -- $flag: $options 90 | or contains -- $flag:: $options 91 | and echo -E "$flag $value" 92 | or echo "Unknown option `$flag'." >&2 93 | end 94 | 95 | # Match an unassoicated value. 96 | case '*' 97 | echo "_ $argv[1]" 98 | end 99 | 100 | set -e argv[1] 101 | end 102 | 103 | # Print out the remeaining values that do not belong to any option. 104 | for arg in $argv 105 | echo "_ $arg" 106 | end 107 | 108 | return 0 109 | end 110 | --------------------------------------------------------------------------------