Flag command line parsing in golang - go

I'm not sure I understand the reasoning behind this example (taken from here), nor what it is trying to communicate about the Go language:
package main
import (
"flag"
"fmt"
)
func main() {
f := flag.NewFlagSet("flag", flag.ExitOnError)
f.Bool("bool", false, "this is bool flag")
f.Int("int", 0, "this is int flag")
visitor := func(a *flag.Flag) {
fmt.Println(">", a.Name, "value=", a.Value)
}
fmt.Println("Visit()")
f.Visit(visitor)
fmt.Println("VisitAll()")
f.VisitAll(visitor)
// set flags
f.Parse([]string{"-bool", "-int", "100"})
fmt.Println("Visit() after Parse()")
f.Visit(visitor)
fmt.Println("VisitAll() after Parse()")
f.VisitAll(visitor)
}
Something along the lines of the setup they have but then adding a
int_val := f.get("int")
to get the named argument would seem more useful. I'm completely new to Go, so just trying to get acquainted with the language.

This is complicated example of using flag package. Typically flags set up this way:
package main
import "flag"
// note, that variables are pointers
var strFlag = flag.String("long-string", "", "Description")
var boolFlag = flag.Bool("bool", false, "Description of flag")
func init() {
// example with short version for long flag
flag.StringVar(strFlag, "s", "", "Description")
}
func main() {
flag.Parse()
println(*strFlag, *boolFlag)
}

Related

Global flags and subcommands

I'm implementing a little CLI with multiple subcommands. I'd like to support global flags, that is flags that apply to all subcommands to avoid repeating them.
For example, in the example below I'm trying to have -required flag that is required for all subcommands.
package main
import (
"flag"
"fmt"
"log"
"os"
)
var (
required = flag.String(
"required",
"",
"required for all commands",
)
fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)
barCmd = flag.NewFlagSet("bar", flag.ExitOnError)
)
func main() {
flag.Parse()
if *required == "" {
fmt.Println("-required is required for all commands")
}
switch os.Args[1] {
case "foo":
fooCmd.Parse(os.Args[2:])
fmt.Println("foo")
case "bar":
barCmd.Parse(os.Args[2:])
fmt.Println("bar")
default:
log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", os.Args[1])
}
}
I would expect usage to be like:
$ go run main.go foo -required helloworld
but if I ran that with the above code I get:
$ go run main.go foo -required hello
-required is required for all commands
flag provided but not defined: -required
Usage of foo:
exit status 2
It looks like flag.Parse() is not capturing -required from the CLI, and then the fooCmd is complaining that I've given it a flag it doesn't recognize.
What's the easiest way to have subcommands with global flags in Golang?
If you intend to implement subcommands, you shouldn't call flag.Parse().
Instead decide which subcommand to use (as you did with os.Args[1]), and call only its FlagSet.Parse() method.
Yes, for this to work, all flag sets should contain the common flags. But it's easy to register them once (in one place). Create a package level variable:
var (
required string
fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)
barCmd = flag.NewFlagSet("bar", flag.ExitOnError)
)
And use a loop to iterate over all flagsets, and register the common flags, pointing to your variable using FlagSet.StringVar():
func setupCommonFlags() {
for _, fs := range []*flag.FlagSet{fooCmd, barCmd} {
fs.StringVar(
&required,
"required",
"",
"required for all commands",
)
}
}
And in main() call Parse() of the appropriate flag set, and test required afterwards:
func main() {
setupCommonFlags()
switch os.Args[1] {
case "foo":
fooCmd.Parse(os.Args[2:])
fmt.Println("foo")
case "bar":
barCmd.Parse(os.Args[2:])
fmt.Println("bar")
default:
log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", os.Args[1])
}
if required == "" {
fmt.Println("-required is required for all commands")
}
}
You can improve the above solution by creating a map of flag sets, so you can use that map to register common flags, and also to do the parsing.
Full app:
var (
required string
fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)
barCmd = flag.NewFlagSet("bar", flag.ExitOnError)
)
var subcommands = map[string]*flag.FlagSet{
fooCmd.Name(): fooCmd,
barCmd.Name(): barCmd,
}
func setupCommonFlags() {
for _, fs := range subcommands {
fs.StringVar(
&required,
"required",
"",
"required for all commands",
)
}
}
func main() {
setupCommonFlags()
cmd := subcommands[os.Args[1]]
if cmd == nil {
log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", os.Args[1])
}
cmd.Parse(os.Args[2:])
fmt.Println(cmd.Name())
if required == "" {
fmt.Println("-required is required for all commands")
}
}
Put the global flags before the subcommand:
go run . -required=x foo.
Use flag.Args() instead of os.Args:
package main
import (
"flag"
"fmt"
"log"
"os"
)
var (
required = flag.String(
"required",
"",
"required for all commands",
)
fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)
barCmd = flag.NewFlagSet("bar", flag.ExitOnError)
)
func main() {
flag.Parse()
if *required == "" {
fmt.Println("-required is required for all commands")
}
args := flag.Args() // everything after the -required flag, e.g. [foo, -foo-flag-1, -foo-flag-2, ...]
switch args[0] {
case "foo":
fooCmd.Parse(args[1:])
fmt.Println("foo")
case "bar":
barCmd.Parse(args[1:])
fmt.Println("bar")
default:
log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", args[0])
}
}
If you want to keep all flags together, after the subcommand, write a helper function that adds common flags to each FlagSet:
var (
fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)
barCmd = flag.NewFlagSet("bar", flag.ExitOnError)
)
type globalOpts struct {
required string
}
func main() {
var opts globalOpts
addGlobalFlags(fooCmd, &opts)
addGlobalFlags(barCmd, &opts)
if opts.required == "" {
fmt.Println("-required is required for all commands")
}
// ...
}
func addGlobalFlags(fs *flag.FlagSet, opts *globalOpts) {
fs.StringVar(
&opts.required,
"required",
"",
"required for all commands",
)
}
Perhaps you can also combine the two approaches to make the global flags work in any position.
Maybe you would be interested in using https://github.com/spf13/cobra - it supports exactly this usecase and many others.

Can anyone help to parse HCL?

I'm going to parse HCL configuration file using this repository.
package main
import (
"fmt"
hclParser "github.com/hashicorp/hcl/hcl/parser"
)
const (
EXAMPLE_CONFIG_STRING = "log_dir = \"/var/log\""
)
func main() {
// parse HCL configuration
if astFile, err := hclParser.Parse([]byte(EXAMPLE_CONFIG_STRING)); err == nil {
fmt.Println(astFile)
} else {
fmt.Println("Parsing failed.")
}
}
How can I parse log_dir in this case?
github.com/hashicorp/hcl/hcl/parser is a low-level package. Use the high-level API instead:
package main
import (
"fmt"
"github.com/hashicorp/hcl"
)
type T struct {
LogDir string `hcl:"log_dir"`
}
func main() {
var t T
err := hcl.Decode(&t, `log_dir = "/var/log"`)
fmt.Println(t.LogDir, err)
}
There is also DecodeObject available if you really want to deal with the AST yourself.

Create struct in one function to be used in another

I am new to Golang and still trying to get my head around structs. I can't seem to figure out how to and create it in one function and use it in another.
Here is my code.
File 1 main.go
package main
import (
"github.com/asolopovas/docker_valet/modules"
"fmt"
)
func main {
fl := dockervalet.GetFlags()
fmt.Pringln(fl.user) // returns fl.user undefined
}
File 2 flags.go
package dockervalet
import (
"flag"
"fmt"
)
type Flags struct {
user string
}
func GetFlags() Flags {
var userFlag string
flag.StringVar(&userFlag, "u", "", "")
flag.StringVar(&userFlag, "user", "", "")
flag.Parse()
fl := Flags{
user: userFlag,
}
fmt.Println(fl.user) // works as expected
return fl
}
Thanks in advance.
Ok, I think I figured it out. The problem wash that I had to use First Capital letters to be able to access this return struct in another function.
File 1 main.go
func main {
fl := GetFlags()
fmt.Pringln(fl.User)
}
File 2 flags.go
type Flags struct {
User string
}
func GetFlags() Flags {
var userFlag string
flag.StringVar(&userFlag, "u", "", "")
flag.StringVar(&userFlag, "user", "", "")
flag.Parse()
fl := Flags{
User: userFlag,
}
fmt.Println(fl.user) // works as expected
return fl
}

In go, how do I make global variables

package main
import (
"fmt"
"bufio"
"os"
)
func main() {
fmt.Print("LOADED!\n")
fmt.Print("insert y value here: ")
inputY := bufio.NewScanner(os.Stdin)
inputY.Scan()
inputXfunc()
}
func inputXfunc() {
fmt.Print("insert x value here: ")
inputX := bufio.NewScanner(os.Stdin)
inputX.Scan()
slope()
}
func slope() {
fmt.Println(inputX.Text())
}
Whenever I run this program, it says, that inputX and inputY are unidentified. How do I make this program use variables that are accessible to all of the functions? All I want to do is devide inputY by inputX then print out the result
I'm just putting my comment as an answer... I would recommend against this however you could just declare the variable at package scope. It would look like this;
package main
import (
"fmt"
"bufio"
"os"
)
var inputX io.Scanner
func main() {
fmt.Print("LOADED!\n")
fmt.Print("insert y value here: ")
inputY := bufio.NewScanner(os.Stdin)
inputY.Scan()
inputXfunc()
}
func inputXfunc() {
fmt.Print("insert x value here: ")
inputX = bufio.NewScanner(os.Stdin) // get rid of assignment initilize short hand here
inputX.Scan()
slope()
}
func slope() {
fmt.Println(inputX.Text())
}
However a better choice would be to change your method definitions to accept arguments and pass the values into them as needed. This would like like so;
func slope(inputX bufio.Scanner) {
fmt.Println(inputX.Text())
}
slope(myInputWhichIsOfTypeIOScanner)
You can create an init() function and make use of it in the main.go by using package like godotenv to set os's environment variables:
global.go file
package global
import (
"log"
"os"
"strconv"
"github.com/joho/godotenv"
)
var (
SERVER_HOST string
SERVER_PORT int
)
func InitConfig() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
SERVER_HOST = os.Getenv("SERVER_HOST")
SERVER_PORT, _ = strconv.Atoi(os.Getenv("SERVER_PORT"))
}
main.go file
package main
import(
G "path/to/config"
)
func init() {
G.InitConfig()
}
func main() {
G.Init()
}
You will still have to import "G" package in other packages to use the variables, but I think the best way to tackle global variables in Go (or any other languages) is to make use of environment variables.

How do you determine the full path of the currently running executable in go?

I've been using this function on osx:
// Shortcut to get the path to the current executable
func ExecPath() string {
var here = os.Args[0]
if !strings.HasPrefix(here, "/") {
here, _ = exec.LookPath(os.Args[0])
if !strings.HasPrefix(here, "/") {
var wd, _ = os.Getwd()
here = path.Join(wd, here)
}
}
return here
}
...but its pretty messy, and it doesn't work on windows at all, and certainly not in git-bash on windows.
Is there a way of doing this cross platform?
NB. Specifically that args[0] depends on how the binary is invoked; it is in some cases only the binary itself, eg. "app" or "app.exe"; so you can't just use that.
This is the traditional way of doing it which I think will work under any platform.
import (
"fmt"
"os"
"path/filepath"
)
// Shortcut to get the path to the current executable
func ExecPath() string {
var here = os.Args[0]
here, err := filepath.Abs(here)
if err != nil {
fmt.Printf("Weird path: %s\n", err)
}
return here
}
I don't think there is a cross-platform way to do this.
However, on OS X, there is a better way. dyld provides a function _NSGetExecutablePath() that gives you the path to the executable. You can call this with CGo.
package main
// #import <mach-o/dyld.h>
import "C"
import (
"fmt"
)
func NSGetExecutablePath() string {
var buflen C.uint32_t = 1024
buf := make([]C.char, buflen)
ret := C._NSGetExecutablePath(&buf[0], &buflen)
if ret == -1 {
buf = make([]C.char, buflen)
C._NSGetExecutablePath(&buf[0], &buflen)
}
return C.GoStringN(&buf[0], C.int(buflen))
}
func main() {
fmt.Println(NSGetExecutablePath())
}

Resources