Argparse for golang. Just because `flag` sucks

Overview

Golang argparse

GoDoc Go Report Card Coverage Status Build Status

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.

Installation

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

Usage

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)
}

Basic options

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", ...)

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(). 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!

Basic Option Structure

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{
			 	Require: false,
				Help: "the input files' folder path",
				Default: "input",
			})

Caveats

There are a few caveats (or more like design choices) to know about:

  • Shorthand arguments MUST be a single character. Shorthand arguments are prepended with single dash "-"
  • If not convenient shorthand argument can be completely skipped by passing empty string "" as first argument
  • Shorthand arguments ONLY for parser.Flag() and parser.FlagCounter() can be combined into single argument same as ps -aux, rm -rf or lspci -vvk
  • Long arguments must be specified and cannot be empty. They are prepended with double dash "--"
  • You cannot define two same arguments. Only first one will be used. For example doing 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
  • There is a pre-defined argument for -h|--help, so from above attempting to define any argument using h as shorthand will fail
  • parser.Parse() returns error in case of something going wrong, but it is not expected to cover ALL cases
  • Any arguments that left un-parsed will be regarded as error

Contributing

Can 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:

  • Add more examples
  • Improve code quality (it is messy right now and could use a major revamp to improve gocyclo report)
  • Add more argument options (such as numbers parsing)
  • Improve test coverage
  • Write a wiki for this project

However note that the logic outlined in method comments must be preserved as the the library must stick with backward compatibility promise!

Acknowledgments

Thanks to Python developers for making a great argparse which inspired this package to match for greatness of Go

Comments
  • Initial implementation of positional arguments

    Initial implementation of positional arguments

    This PR concerns #20 (Positional Arguments issue)

    This PR is solely to provide a prototype for a grounded discussion on implementation of positional arguments.

    @akamensky I am eager to hear your thoughts on this methodology for positional arguments. Let me know what you think!

    opened by vsachs 21
  • Add positional argument type

    Add positional argument type

    Add the ability to accept positional arguments by adding a parser.Positional() argument type that simply collects all of the remaining unparsed arguments and returns them as a list of strings. Example code in examples/positionals/positionals.go. Still needs tests.

    The Nargs additions that @jokelyo made in http://github.com/jokelyo/argparse also look promising; I considered extending that to get positionals. The upside there would be the ability to handle more positional types than just strings; the downside would be a much larger code delta.

    stale-issue 
    opened by stevegt 16
  • Repeated text in sub-command usage text

    Repeated text in sub-command usage text

    Thank you for the great library! As a former python argparse user this has helped getting a CLI together for some go work.

    Working on a program with two levels of sub-commands I noticed the help info repeats with incorrect/incomplete argument usage. This is the not the case with doing -h on the sub-command.

    This does not seem to be the same issue as #5 but might be related?

    The command-advanced in examples shows this same behavior:

    Repeated:

    [email protected]:~/code/argparse/examples/commands-advanced$ ./commands-advanced dog
    [sub]Command required
    usage: zooprog dog <Command> [--wiggle] [-h|--help] --name "<value>"
    
                   We are going to see dog
    
    Commands:
    
      speak   Make the dog speak
      feed    Make the dog eat
      summon  Make the dog come over
      play    Make the dog play
    
    Arguments:
    
          --wiggle  Makes the dog to wiggle its tail
      -h  --help    Print help information
          --name    Provide an optional name for the animal
    
    usage: zooprog dog <Command> [--wiggle] [-h|--help] --name "<value>"
    
                   We are going to see dog
    
    Commands:
    
      speak   Make the dog speak
      feed    Make the dog eat
      summon  Make the dog come over
      play    Make the dog play
    
    Arguments:
    
          --wiggle  Makes the dog to wiggle its tail
      -h  --help    Print help information
          --name    Provide an optional name for the animal
    

    OK:

    [email protected]:~/code/argparse/examples/commands-advanced$ ./commands-advanced dog -h
    usage: zooprog dog <Command> [--wiggle] [-h|--help] --name "<value>"
    
                   We are going to see dog
    
    Commands:
    
      speak   Make the dog speak
      feed    Make the dog eat
      summon  Make the dog come over
      play    Make the dog play
    
    Arguments:
    
          --wiggle  Makes the dog to wiggle its tail
      -h  --help    Print help information
          --name    Provide an optional name for the animal
    
    bug help wanted Test case missing 
    opened by skaymakca 12
  • --help flag is being ignored when required arguments are not provided with sub-command

    --help flag is being ignored when required arguments are not provided with sub-command

    i.e. progname command --help will yield error message instead of printing entire help message.

    Help message should be printed whenever -h|--help is provided regardless whether on top level or on sub-command.

    bug help wanted 
    opened by akamensky 11
  • testFunction

    testFunction

    I thought it would be nice to include a test function in the options. If it is defined, when creating the argument, you should test the received value of the argument with its help. If it fails, finish processing with an error. This will help the user to handle a wide range of situations when the list of values ​​is limited, not providing for all cases in the library itself.

    stale-issue 
    opened by goofinator 11
  • Help Command closes Program - undesirable in web chat app parsing

    Help Command closes Program - undesirable in web chat app parsing

    Hi i am trying to build a discord chat bot, and leveraging this package has been awesome for that, however when the parser is created a non overridable help argument is added which os.Exit(0) when triggered which is meaning users chatting the bot can make it close. I know this is probably a unexpected use case, i am willing to fork the project and workout a solution if you would like to give my guidance on how the end state should look.

    enhancement help wanted 
    opened by zanven42 10
  • Missing Features

    Missing Features

    While I'm not entirely convinced that all features of argparse are useful, a few are really nifty.

    Making a list of few features that I think could be added:

    • [ ] Positional arguments
      • Take arguments based on the position.
    • [ ] nargs functionality
      • Take constant or variable number of arguments that follow
      • Important for optional arguments
      • Does not make much sense for positional arguments
    • [ ] Repeated flags/options
      • Allow to repeat flags or optional arguments to specify more inputs
      • Support for multiple levels like -vvv
    help wanted 
    opened by JaikrishnaTS 10
  • Provide ability to configure helptext maxWidth columns

    Provide ability to configure helptext maxWidth columns

    The usage helptext is currently very cramped, at 80 maximum characters ( https://github.com/akamensky/argparse/blob/master/argparse.go#L647 ):

    Screen Shot 2021-09-18 at 2 58 36 PM

    It would be great if this were configurable

    stale-issue 
    opened by mieubrisse 9
  • Thoughts on a Lists

    Thoughts on a Lists

    There is a question. There are arguments with a parameter for different types (String, Float, Int). There is also an argument of type List, which allows you to create a list of parameters of type String. Wouldn't it be logical to make StringList, FloatList, IntList?

    enhancement 
    opened by goofinator 8
  • [bug] Unable to detect if file flag was actually provided?

    [bug] Unable to detect if file flag was actually provided?

    Hi There.

    This has been my go-to library for argument parsing, and I recently tried to use this to parse file arguments. However, it seems impossible to determine if a flag was actually provided. For example:

    configFile := parser.File("c", "config", etc)
    parser.Parse()
    if configFile != nil {
        // Do stuff with the file
    }
    // Do stuff if -c wasn't provided
    

    This does not work, because the configFile is always non-nil, as the parser seems to work by overwriting the internal structures of os.File, and if the argument isn't provided, the configFile will still be non-nil. Furthermore, the standard library doesn't help in this regard, since all the regular functions assume that the file object is valid, so if I try something like configFile.Name(), it just panics.

    Is there something I'm missing here, or is there no way to distinguish if the -c flag was provided, without reflection nonsense or panic handling?

    bug 
    opened by rsheasby 7
  • Arg can return the result pointer

    Arg can return the result pointer

    This allows for more dynamic flag/arg handling, such as scenarios where:

    • multiple commands each have their own unique flags that you don't want to track individually
    • multiple commands share some subset of flags that don't fit on the main parser
    ~/git/github.com/vsachs/argparse| go test
    PASS
    ok      github.com/akamensky/argparse   0.015s
    
    opened by vsachs 7
  • enable required for positionals

    enable required for positionals

    Positionals (added in 1.4) currently ignore the Required option. It should be supported, with these basic requirements:

    • Required positionals must have a value supplied by the user (not by Default) or an error is thrown
    • If a positional is set to required, throw an error if there are already any positionals on this command which are NOT required -- This is necessary in order to avoid ambiguity of positional argument satisfaction: Say you have two positionals, the first is optional and the second is required. The user gives one value on the CLI. Which positional gets the value? Does the required positional get it IFF the optional has a default? -- Do not throw an error if there are optional positionals on a parent command of the command which has had Required positionals added
    • Allow optional positionals to follow Required positionals
    • Do not throw an error if there are Required positionals on commands which did not Happen

    Some discussion of the problem is warranted before coding begins. This may not be an exhaustive list of requirements.

    enhancement good first issue 
    opened by vsachs 0
  • expect File arg to take standard input / output stream as a value

    expect File arg to take standard input / output stream as a value

    When I define a File arg (like following) to specify an input file,

    -i <input-file>
    

    I wish we can specify the - value to input from the standard input stream. However, it will report error now.

    if it's defined with a String arg, the help message will display like -i "<value>"(cannot tell this should be a file), not as expected very much.

    It is the same case for the output file. I wish to specify the - value to output to standard output stream.

    help wanted good first issue 
    opened by xnslong 2
  • support

    support "h" custom argument

    If I define

    host := parser.String("h", "host", &argparse.Options{Required: false, Help: "listening host", Default: "127.0.0.1"})
    // ...
    

    I'll get a panic ->

    panic: unable to add String: short name h occurs more than once

    This is because there's a built-in -h/--help already occupied it.

    For now I can workaround it by using a different letter, but It would be a nice to have support for h 😁

    enhancement help wanted Test case missing 
    opened by jossef 2
  • Handle better newlines in help message

    Handle better newlines in help message

    Hello,

    I want to add a list to help message, so each item should be on a new line. Now this looks not very good:

          --input          Possible values:
    not set - read from stdin;
    gs://<path to file gzipped file> - read from
                           GCS;
    pubsub://projects/<project>/subscriptions/<subscription> - fetch input value
                           from PubSub
    

    While the original help string looks like:

    	Help: "Possible values:\n" +
    		"not set - read from stdin;\n" +
    		"gs://<path to file gzipped file> - read from GCS;\n" +
    		"pubsub://projects/<project>/subscriptions/<subscription> - fetch input value from PubSub"
    
    enhancement help wanted 
    opened by simplylizz 1
Releases(v1.4.0)
  • v1.4.0(Aug 9, 2022)

    What's Changed

    • Initial implementation of positional arguments by @vsachs in https://github.com/akamensky/argparse/pull/102

    Full Changelog: https://github.com/akamensky/argparse/compare/v1.3.3...v1.4.0

    Source code(tar.gz)
    Source code(zip)
  • v1.3.3(Aug 3, 2022)

    What's Changed

    • Prefix validation error with argument name by @look in https://github.com/akamensky/argparse/pull/105

    New Contributors

    • @look made their first contribution in https://github.com/akamensky/argparse/pull/105

    Full Changelog: https://github.com/akamensky/argparse/compare/v1.3.2...v1.3.3

    Source code(tar.gz)
    Source code(zip)
  • v1.3.2(Jul 28, 2022)

    What's Changed

    • Fix coverage reporting by @akamensky in https://github.com/akamensky/argparse/pull/101
    • Flag ignores default value by @akamensky in https://github.com/akamensky/argparse/pull/103
    • Flag counter usage fix by @Alec-Bakholdin in https://github.com/akamensky/argparse/pull/100
    • When subcommand must be present but not provided the error must be triggered by @akamensky in https://github.com/akamensky/argparse/pull/104

    Full Changelog: https://github.com/akamensky/argparse/compare/v1.3.1...v1.3.2

    Source code(tar.gz)
    Source code(zip)
  • v1.3.1(Aug 13, 2021)

  • v1.2.2(Aug 25, 2020)

Owner
Alexey Kamenskiy
Alexey Kamenskiy
An implementation of sed in Go. Just because!

Sed-Go An implementation of sed in Go. Just because! Status Command-Line processing: Done. It accepts '-e', '-f', '-n' and long versions of the same.

rwtodd 115 Aug 23, 2022
V2 because I wasn't happy with the way things were going in V1

Pokerchips V2 A client/server app that simulates poker chips so you and your friends can play poke without physical chips! Client The client is writte

Anders Olson 0 Jan 11, 2022
Golang library with POSIX-compliant command-line UI (CLI) and Hierarchical-configuration. Better substitute for stdlib flag.

cmdr cmdr is a POSIX-compliant, command-line UI (CLI) library in Golang. It is a getopt-like parser of command-line options, be compatible with the ge

hz 112 Aug 14, 2022
CONTRIBUTIONS ONLY: A Go (golang) command line and flag parser

CONTRIBUTIONS ONLY What does this mean? I do not have time to fix issues myself. The only way fixes or new features will be added is by people submitt

Alec Thomas 3.3k Sep 21, 2022
The standard library flag package with its missing features

cmd Package cmd is a minimalistic library that enables easy sub commands with the standard flag library. This library extends the standard library fla

Eyal Posener 33 Sep 23, 2021
Flag is a simple but powerful command line option parsing library for Go support infinite level subcommand

Flag Flag is a simple but powerful commandline flag parsing library for Go. Documentation Documentation can be found at Godoc Supported features bool

null 122 Sep 1, 2022
A collection of CLI argument types for the Go `flag` package.

flagvar A collection of CLI argument types for the flag package. import "github.com/sgreben/flagvar" Or just copy & paste what you need. It's public d

Sergey Grebenshchikov 41 Sep 26, 2022
Drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags.

Description pflag is a drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags. pflag is compatible with the GNU extensions to

Steve Francia 1.9k Sep 19, 2022
Go binding configuration and command flag made easy✨✨

✨ Binding configuration and command flag made easy! ✨ You can use multiple keys tag to simplify the look like this (supported feature**): // single ta

Ii64人 16 Sep 18, 2022
Golisp-wtf - A lisp interpreter (still just a parser) implementation in golang. You may yell "What the fuck!?.." when you see the shitty code.

R6RS Scheme Lisp dialect interpreter This is an implementation of a subset of R6RS Scheme Lisp dialect in golang. The work is still in progress. At th

Vladimir Novikov 0 Jan 7, 2022
Just a simple CLI tool to group dependabot PRs by dependency and merge them.

Dependabotbot Have you been the victim of a lodash update? Has your notification page in Github been assaulted by needing to update a patch version of

Chris Griffing 22 Jun 30, 2022
It is an easy and fast tool to install your packages with just one command.

Trouxa It is an easy and fast tool to install your packages with just one command. What means "Trouxa"? In portuguese, Trouxa means something like a "

Baianoware 7 Feb 13, 2022
Get Brew Packages to update, just like you've experienced from Ubuntu

Get the number of Brew Packages to update, just like you've experienced from Ubuntu BrewUpdate is a simple utility written in Go, notify you how many

YeonGyu-Kim 13 Nov 6, 2021
react, fiber, api just for saying hello

say-hello Introduction Sometimes we need to have a frontend besides the backend. This repository uses React to show how we can server a React build an

Cloud Native Go by Example 8 Jun 18, 2022
Count once - Just once? no, when appear many it run once, but it can run many times

countOnce just once? no, when appear many it run once, but it can run many times

null 1 Jan 29, 2022
Turn .mp3 files in current directory to a podcast feed just one command.

dir2cast Turn .mp3 files in current directory to a podcast feed just one command. Then you can subscribe to it with your favorite podcast client, down

王子亭 4 Jun 27, 2022
Fully featured Go (golang) command line option parser with built-in auto-completion support.

go-getoptions Go option parser inspired on the flexibility of Perl’s GetOpt::Long. Table of Contents Quick overview Examples Simple script Program wit

David Gamba 46 Sep 26, 2022
A CLI tool implemented by Golang to manage `CloudComb` resource

CloudComb CLI tool: comb Get Started comb is a CLI tool for manage resources in CloudComb base on cloudcomb-go-sdk. Support Mac, Linux and Windows. We

Bingo Huang 22 Jan 4, 2021
Automatically generate Go (golang) struct definitions from example JSON

gojson gojson generates go struct definitions from json or yaml documents. Example $ curl -s https://api.github.com/repos/chimeracoder/gojson | gojson

Aditya Mukerjee 2.5k Sep 26, 2022