# flag package
[flag package][flag-pkg] is the Go equivalent of Python [argparse][python-argparse]. While not as powerful, it does what we expect it to do. It simplifies adding and parsing command line parameters, leaving us to concentrate on the tools. Most of our tools will need them to be actually useful (hardcoding URLs and IPs get old too fast).

<!-- MarkdownTOC -->

- [Alternative community packages](#alternative-community-packages)
- [Basic flags](#basic-flags)
    - [Flag use](#flag-use)
- [Declaring flags in the init function](#declaring-flags-in-the-init-function)
- [Custom flag types and multiple values](#custom-flag-types-and-multiple-values)
- [Required flags](#required-flags)
- [Alternate and shorthand flags](#alternate-and-shorthand-flags)
- [Non-flag arguments](#non-flag-arguments)
- [Subcommands](#subcommands)

<!-- /MarkdownTOC -->

<a name="alternative-community-packages"></a>
## Alternative community packages
Some community packages offer what `flag` does and more. In this guide I am trying to stick to the standard library. Some of these packages are:

- [Cobra][cobra-github]: A Commander for modern Go CLI interactions
- [cli][cli-github]: A simple, fast, and fun package for building command line apps in Go

<a name="basic-flags"></a>
## Basic flags
Declaring basic flags is easy. We can create basic types such as: `string`, `bool` and `int`.

A new flag is easy to add:

- `ipPtr := flag.String("ip", "127.0.0.1", "target IP")`
    + `String`: Flag type.
    + `ipPtr`: Pointer to flag's value.
    + `ip`: Flag name, meaning flag can be called with `-ip`.
    + `127.0.0.1`: Flag's default value if not provided.
    + `target IP`: Flag description, displayed with `-h` switch.

It's also possible to pass a pointer directly:

- `var port int`
- `flag.IntVar(&port, "port", 8080, "Port")`

``` go
// 03.1-01-flag1.go
package main

import (
    "flag"
    "fmt"
)

func main() {

    // Declare flags
    // Remember, flag methods return pointers
    ipPtr := flag.String("ip", "127.0.0.1", "target IP")

    var port int
    flag.IntVar(&port, "port", 8080, "Port")

    verbosePtr := flag.Bool("verbose", true, "verbosity")

    // Parse flags
    flag.Parse()

    // Hack IP:port
    fmt.Printf("Hacking %s:%d!\n", *ipPtr, port)

    // Display progress if verbose flag is set
    if *verbosePtr {
        fmt.Printf("Pew pew!\n")
    }
}
```

This program contains a mistake! Can you spot it? If not, don't worry.

`-h/-help` print usage:

```
$ go run 03.1-01-flag1.go -h
Usage of ... \_obj\exe\03.1-01-flag1.exe:
  -ip string
        target IP (default "127.0.0.1")
  -port int
        Port (default 8080)
  -verbose
        verbosity (default true)
exit status 2
```

Without any flags, default values are used:

```
$ go run 03.1-01-flag1.go
Hacking 127.0.0.1:8080!
Pew pew!
```

<a name="flag-use"></a>
### Flag use
Flag use is standard.

```
$ go run 03.1-01-flag1.go -ip 10.20.30.40 -port 12345
Hacking 10.20.30.40:12345!
Pew pew!
```

The problem is the default value of our boolean flag. A boolean flag is `true` if it occurs and `false` if it. We set the default value of `verbose` to `true` meaning with our current knowledge we cannot set verbose to `false` (we will see how below but it's not idiomatic).

Fix that line and run the program again:

```
$ go run 03.1-02-flag2.go -ip 10.20.30.40 -port 12345
Hacking 10.20.30.40:12345!

$ go run 03.1-02-flag2.go -ip 10.20.30.40 -port 12345 -verbose
Hacking 10.20.30.40:12345!
Pew pew!
```

`=` is allowed. Boolean flags can also be set this way (only way to set verbose to `false` in our previous program):

```
$ go run 03.1-02-flag2.go -ip=20.30.40.50 -port=54321 -verbose=true
Hacking 20.30.40.50:54321!
Pew pew!

$ go run 03.1-02-flag2.go -ip=20.30.40.50 -port=54321 -verbose=false
Hacking 20.30.40.50:54321!
```

`--flag` is also possible:

```
$ go run 03.1-02-flag2.go --ip 20.30.40.50 --port=12345 --verbose
Hacking 20.30.40.50:12345!
Pew pew!
```

<a name="declaring-flags-in-the-init-function"></a>
## Declaring flags in the init function
`init` function is a good location to declare flags. `init` function is executed after variable initialization values and before `main`. There's one little catch, variables declared in `init` are out of focus outside (and in `main`) hence we need to declare variables outside and use `*Var` methods:

``` go
package main

import (
    "flag"
    "fmt"
)

// Declare flag variables
var (
    ip      string
    port    int
    verbose bool
)

func init() {
    // Declare flags
    // Remember, flag methods return pointers
    flag.StringVar(&ip, "ip", "127.0.0.1", "target IP")

    flag.IntVar(&port, "port", 8080, "Port")

    flag.BoolVar(&verbose, "verbose", false, "verbosity")
}

func main() {

    // Parse flags
    flag.Parse()

    // Hack IP:port
    fmt.Printf("Hacking %s:%d!\n", ip, port)

    // Display progress if verbose flag is set
    if verbose {
        fmt.Printf("Pew pew!\n")
    }
}
```

<a name="custom-flag-types-and-multiple-values"></a>
## Custom flag types and multiple values
Custom flag types are a bit more complicated. Each custom type needs to implement the [flag.Value][flag-value-interface] interface. This interface has two methods:

``` go
type Value interface {
        String() string
        Set(string) error
}
```

In simple words:

1. Create a new type `mytype`.
2. Create two methods with `*mytype` receivers named `String()` and `Set()`.
    - `String()` casts the custom type to a `string` and returns it.
    - `Set(string)` has a `string` argument and populates the type and returns an error if applicable.
3. Create a new flag without an initial value:
    - Call `flag.NewFlagSet(&var, ` instead of `flag.String(`.
    - Call `flag.Var(` instead of `flag.StringVar(` or `flag.IntVar(`.

Now we can modify our previous example to accept multiple comma-separated IPs. Note, we are using the same structure of generateStrings and consumeString from section [02.6 - sync.WaitGroup][sync-waitgroup]. In short, we are going to generate all permutations of IP:ports and then "hack" each of them in one goroutine.

The permutation happens in its own goroutine and is results are sent to channel one by one. When all permutations are generated, channel is closed.

In main, we read from channel and spawn a new goroutine to hack each IP:port. When channel is closed, we wait for all goroutines to finish and then return.

``` go
package main

import (
    "errors"
    "flag"
    "fmt"
    "strings"
    "sync"
)

// 1. Create a custom type from a string slice
type strList []string

// 2.1 implement String()
func (str *strList) String() string {
    return fmt.Sprintf("%v", *str)
}

// 2.2 implement Set(*strList)
func (str *strList) Set(s string) error {
    // If input was empty, return an error
    if s == "" {
        return errors.New("nil input")
    }
    // Split input by ","
    *str = strings.Split(s, ",")
    // Do not return an error
    return nil
}

// Declare flag variables
var (
    ip      strList
    port    strList
    verbose bool
)

var wg sync.WaitGroup

func init() {
    // Declare flags
    // Remember, flag methods return pointers
    flag.Var(&ip, "ip", "target IP")

    flag.Var(&port, "port", "Port")

    flag.BoolVar(&verbose, "verbose", false, "verbosity")
}

// permutations creates all permutations of ip:port and sends them to a channel.
// This is preferable to returing a []string because we can spawn it in a
// goroutine and process items in the channel while it's running. Also save
// memory by not creating a large []string that contains all permutations.
func permutations(ips strList, ports strList, c chan<- string) {

    // Close channel when done
    defer close(c)
    for _, i := range ips {
        for _, p := range ports {
            c <- fmt.Sprintf("%s:%s", i, p)
        }
    }
}

// hack spawns a goroutine that "hacks" each target.
// Each goroutine prints a status and display progres if verbose is true
func hack(target string, verbose bool) {

    // Reduce waitgroups counter by one when hack finishes
    defer wg.Done()
    // Hack the planet!
    fmt.Printf("Hacking %s!\n", target)

    // Display progress if verbose flag is set
    if verbose {
        fmt.Printf("Pew pew!\n")
    }
}

func main() {

    // Parse flags
    flag.Parse()

    // Create channel for writing and reading IP:ports
    c := make(chan string)

    // Perform the permutation in a goroutine and send the results to a channel
    // This way we can start "hacking" during permutation generation and
    // not create a huge list of strings in memory
    go permutations(ip, port, c)

    for {
        select {
        // Read a string from channel
        case t, ok := <-c:
            // If channel is closed
            if !ok {
                // Wait until all goroutines are done
                wg.Wait()
                // Print hacking is finished and return
                fmt.Println("Hacking finished!")
                return
            }
            // Otherwise increase wg's counter by one
            wg.Add(1)
            // Spawn a goroutine to hack IP:port read from channel
            go hack(t, verbose)
        }
    }
}
```

Result:

```
$ go run 03.1-04-flag4.go -ip 10.20.30.40,50.60.70.80 -port 1234
Hacking 50.60.70.80:1234!
Hacking 10.20.30.40:1234!
Hacking finished!

$ go run 03.1-04-flag4.go -ip 10.20.30.40,50.60.70.80 -port 1234,4321 
Hacking 10.20.30.40:4321!
Hacking 10.20.30.40:1234!
Hacking 50.60.70.80:4321!
Hacking 50.60.70.80:1234!
Hacking finished!

$ go run 03.1-04-flag4.go -ip 10.20.30.40,50.60.70.80 -port 1234,4321 -verbose
Hacking 10.20.30.40:4321!
Pew pew!
Hacking 50.60.70.80:4321!
Pew pew!
Hacking 10.20.30.40:1234!
Pew pew!
Hacking 50.60.70.80:1234!
Pew pew!
Hacking finished!
```

<a name="required-flags"></a>
## Required flags
`flag` does not support this. In Python we can use `parser.add_mutually_exclusive_group()`. Instead we have to manually check if a flag is set. This can be done by comparing a flag with it's default value or the initial zero value of type in case it does not have a default value.

This can get complicated when the flag can contain the zero value. For example an `int` flag could be set with value `0` which is the same as the default value for `int`s. Something that can help is the number of flags after parsing available from `flag.NFlag()`. If number of flags is less than expected, we know something is wrong.

<a name="alternate-and-shorthand-flags"></a>
## Alternate and shorthand flags
`flag` does not have support for shorthand or alternate flags. They need to be declared in a separate statement.

``` go
flag.BoolVar(&verbose, "verbose", false, "verbosity")
flag.BoolVar(&verbose, "v", false, "verbosity")
```

<a name="non-flag-arguments"></a>
## Non-flag arguments
After `flag.Parse()` it's possible to read other arguments passed to the application with `flag.Args()`. The number of them is available from `flag.NArg()` and they individually can be accessed by index using `flag.Arg(i)`.

``` go
// 03.1-05-args.go
package main

import (
    "flag"
    "fmt"
)

func main() {
    // Set flag
    _ = flag.Int("flag1", 0, "flag1 description")
    // Parse all flags
    flag.Parse()
    // Enumererate flag.Args()
    for _, v := range flag.Args() {
        fmt.Println(v)
    }
    // Enumerate using flag.Arg(i)
    for i := 0; i < flag.NArg(); i++ {
        fmt.Println(flag.Arg(i))
    }
}
```

Running the program with non-flag arguments results in:

```
$ go run 03.1-05-flag5.go -flag1 12 one two 3
one
two
3
one
two
3
```

<a name="subcommands"></a>
## Subcommands
Subcommands are possible using [flag.NewFlagSet][flag-newflagset].

- `func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet`

We can decide what happens if parsing that subcommand fails with the second parameter:

``` go
const (
    ContinueOnError ErrorHandling = iota // Return a descriptive error.
    ExitOnError                          // Call os.Exit(2).
    PanicOnError                         // Call panic with a descriptive error.
)
```

After that we need to parse the subcommand. This is usually done by reading `os.Args[1]` (second argument after program name should be subcommand) and parsing the detected subcommand.

``` go
// 03.1-06-subcommand.go
package main

import (
    "flag"
    "fmt"
    "os"
)

var (
    sub1 *flag.FlagSet
    sub2 *flag.FlagSet

    sub1flag  *int
    sub2flag1 *string
    sub2flag2 int

    usage string
)

func init() {
    // Declare subcommand sub1
    sub1 = flag.NewFlagSet("sub1", flag.ExitOnError)
    // int flag for sub1
    sub1flag = sub1.Int("sub1flag", 0, "subcommand1 flag")

    // Declare subcommand sub2
    sub2 = flag.NewFlagSet("sub2", flag.ContinueOnError)
    // string flag for sub2
    sub2flag1 = sub2.String("sub2flag1", "", "subcommand2 flag1")
    // int flag for sub2
    sub2.IntVar(&sub2flag2, "sub2flag2", 0, "subcommand2 flag2")
    // Create usage
    usage = "sub1 -sub1flag (int)\nsub2 -sub2flag1 (string) -sub2flag2 (int)"
}

func main() {
    // If subcommand is not provided, print error, usage and return
    if len(os.Args) < 2 {
        fmt.Println("Not enough parameters")
        fmt.Println(usage)
        return
    }

    // Check the sub command
    switch os.Args[1] {

    // Parse sub1
    case "sub1":
        sub1.Parse(os.Args[2:])

    // Parse sub2
    case "sub2":
        sub2.Parse(os.Args[2:])

    // If subcommand is -h or --help
    case "-h":
        fallthrough
    case "--help":
        fmt.Printf(usage)
        return
    default:
        fmt.Printf("Invalid subcommand %v", os.Args[1])
        return
    }

    // If sub1 was provided and parse, print the flags
    if sub1.Parsed() {
        fmt.Printf("subcommand1 with flag %v\n", *sub1flag)
        return
    }

    // If sub2 was provided and parse, print the flags
    if sub2.Parsed() {
        fmt.Printf("subcommand2 with flags %v, %v\n", *sub2flag1, sub2flag2)
        return
    }
}
```

As you can see there's a lot of manual work in sub commands and they are not as elegant as normal flags.

#### Continue reading ⇒ [03.2 - log package](03.2.md)

<!-- Links -->

[flag-pkg]: https://godoc.org/flag
[python-argparse]: https://docs.python.org/2/howto/argparse.html
[flag-value-interface]: https://godoc.org/flag#Value
[sync-waitgroup]: 02.6.md#syncwaitgroup
[flag-newflagset]: https://godoc.org/flag#NewFlagSet
[cobra-github]: https://github.com/spf13/cobra
[cli-github]: https://github.com/urfave/cli