Let's be honest -- Go's standard command line arguments parser flag
terribly sucks.
It cannot come anywhere close to the Python's argparse
module. This is why this project exists.
The goal of this project is to bring ease of use and flexibility of argparse
to Go.
Which is where the name of this package comes from.
To install and start using argparse simply do:
$ go get -u -v github.com/akamensky/argparse
You are good to go to write your first command line tool! See Usage and Examples sections for information how you can use it
To start using argparse in Go see above instructions on how to install.
From here on you can start writing your first program.
Please check out examples from examples/
directory to see how to use it in various ways.
Here is basic example of print command (from examples/print/
directory):
package main
import (
"fmt"
"github.com/akamensky/argparse"
"os"
)
func main() {
// Create new parser object
parser := argparse.NewParser("print", "Prints provided string to stdout")
// Create string flag
s := parser.String("s", "string", &argparse.Options{Required: true, Help: "String to print"})
// Parse input
err := parser.Parse(os.Args)
if err != nil {
// In case of error print error and print usage
// This can also be done by passing -h or --help flags
fmt.Print(parser.Usage(err))
}
// Finally print the collected string
fmt.Println(*s)
}
Create your parser instance and pass it program name and program description.
Program name if empty will be taken from os.Args[0]
(which is okay in most cases).
Description can be as long as you wish and will be used in --help
output
parser := argparse.NewParser("progname", "Description of my awesome program. It can be as long as I wish it to be")
String will allow you to get a string from arguments, such as $ progname --string "String content"
var myString *string = parser.String("s", "string", ...)
Positional arguments can be used like this $ progname value1
.
See Basic Option Structure and Positionals.
var myString *string = parser.StringPositional(nil)
var myString *string = parser.FilePositional(nil)
var myString *string = parser.FloatPositional(nil)
var myString *string = parser.IntPositional(nil)
var myString *string = parser.SelectorPositional([]string{"a", "b"}, nil)
var myString1 *string = parser.StringPositional(Options{Default: "beep"})
Selector works same as a string, except that it will only allow specific values.
For example like this $ progname --debug-level WARN
var mySelector *string = parser.Selector("d", "debug-level", []string{"INFO", "DEBUG", "WARN"}, ...)
StringList allows to collect multiple string values into the slice of strings by repeating same flag multiple times.
Such as $ progname --string hostname1 --string hostname2 -s hostname3
var myStringList *[]string = parser.StringList("s", "string", ...)
List allows to collect multiple values into the slice of strings by repeating same flag multiple times
(at fact - it is an Alias of StringList).
Such as $ progname --host hostname1 --host hostname2 -H hostname3
var myList *[]string = parser.List("H", "hostname", ...)
Flag will tell you if a simple flag was set on command line (true is set, false is not).
For example $ progname --force
var myFlag *bool = parser.Flag("f", "force", ...)
FlagCounter will tell you the number of times that simple flag was set on command line
(integer greater than or equal to 1 or 0 if not set).
For example $ progname -vv --verbose
var myFlagCounter *int = parser.FlagCounter("v", "verbose", ...)
Int will allow you to get a decimal integer from arguments, such as $ progname --integer "42"
var myInteger *int = parser.Int("i", "integer", ...)
IntList allows to collect multiple decimal integer values into the slice of integers by repeating same flag multiple times.
Such as $ progname --integer 42 --integer +51 -i -1
var myIntegerList *[]int = parser.IntList("i", "integer", ...)
Float will allow you to get a floating point number from arguments, such as $ progname --float "37.2"
var myFloat *float64 = parser.Float("f", "float", ...)
FloatList allows to collect multiple floating point number values into the slice of floats by repeating same flag multiple times.
Such as $ progname --float 42 --float +37.2 -f -1.0
var myFloatList *[]float64 = parser.FloatList("f", "float", ...)
File will validate that file exists and will attempt to open it with provided privileges.
To be used like this $ progname --log-file /path/to/file.log
var myLogFile *os.File = parser.File("l", "log-file", os.O_RDWR, 0600, ...)
FileList allows to collect files into the slice of files by repeating same flag multiple times.
FileList will validate that files exists and will attempt to open them with provided privileges.
To be used like this $ progname --log-file /path/to/file.log --log-file /path/to/file_cpy.log -l /another/path/to/file.log
var myLogFiles *[]os.File = parser.FileList("l", "log-file", os.O_RDWR, 0600, ...)
You can implement sub-commands in your CLI using parser.NewCommand()
or go even deeper with command.NewCommand()
.
Addition of a sub-command implies that a subcommand is required.
Sub-commands are always parsed before arguments.
If a command has Positional
arguments and sub-commands then sub-commands take precedence.
Since parser inherits from command, every command supports exactly same options as parser itself,
thus allowing to add arguments specific to that command or more global arguments added on parser itself!
You can also dynamically retrieve argument values and if they were parsed:
var myInteger *int = parser.Int("i", "integer", ...)
parser.Parse()
fmt.Printf("%d", *parser.GetArgs()[0].GetResult().(*int))
fmt.Printf("%v", *parser.GetArgs()[0].GetParsed())
The Option
structure is declared at argparse.go
:
type Options struct {
Required bool
Validate func(args []string) error
Help string
Default interface{}
}
You can set Required
to let it know if it should ask for arguments.
Or you can set Validate
as a lambda function to make it know while value is valid.
Or you can set Help
for your beautiful help document.
Or you can set Default
will set the default value if user does not provide a value.
Example:
dirpath := parser.String("d", "dirpath",
&argparse.Options{
Required: false,
Help: "the input files' folder path",
Default: "input",
})
There are a few caveats (or more like design choices) to know about:
"-"
""
as first argumentparser.Flag()
and parser.FlagCounter()
can be combined into single argument same as ps -aux
, rm -rf
or lspci -vvk
"--"
parser.Flag("t", "test", nil)
followed by parser.String("t", "test2", nil)
will not work as second String
argument will be ignored (note that both have "t"
as shorthand argument). However since it is case-sensitive library, you can work arounf it by capitalizing one of the arguments-h|--help
, so from above attempting to define any argument using h
as shorthand will failparser.Parse()
returns error in case of something going wrong, but it is not expected to cover ALL casesPositional
args have a set of effects and conditions:
Happened
Happened
it repeatsCan you write in Go? Then this projects needs your help!
Take a look at open issues, specially the ones tagged as help-wanted
.
If you have any improvements to offer, please open an issue first to ensure this improvement is discussed.
There are following tasks to be done:
However note that the logic outlined in method comments must be preserved as the the library must stick with backward compatibility promise!
Thanks to Python developers for making a great argparse
which inspired this package to match for greatness of Go