You might not need Cobra
Go Flag, and Flags
AJ ONeal
@_beyondcode
twitch.tv/coolaj86
github.com/therootcompany
Deep-Learner
Dangerous Wrong Thinker
Gun for Hire
🐹 Go 📦 Node 🦎 Zig
🛜 Net 🔐 Sec 🐧 Proxmox
Utah Colo on Meetup
-flag
--flag // double dashes are also permitted
-flag=x
-flag x // non-boolean flags only
A Long Time Ago
In a Shell, Far, Far Away...
POSIX
- Strings
- Flags
- Options (GNU)
- Arguments
https://gist.github.com/coolaj86/1759b70e72f038869b7bf87816d9dc2e
1. POSIX Strings
expression
'string literal'
"string expression"
Whitespace-delimited
echo foo bar baz
echo foo bar baz
echo ' foo bar baz '
String Literals
Same as Go's `
string literals:
- absolutely literal
- NO escapes
String Literals
echo '$foo \'
echo 'bar
baz'
String Expressions
Handle escape sequences, and $
, $(...)
, ${...}
expressions.
String Expressions
echo "$foo"
echo "Filename: $(date '+%F_%H.%M.%S')"
String Expr vs Expr
echo "$(echo ' foo bar baz ')"
echo $(echo ' foo bar baz ')
Expressions
All of the goodies of string expressions, plus...
echo
echo $PATH
echo (echo "Hello, World!")
{
printf 'Hello, '
echo 'World!'
} > greeting.txt
cmd_curl="$(echo "curl -fsSL")"
$cmd_curl 'https://example.com'
"$cmd_curl" 'https://example.com'
Pop Quiz!
echo string' # not a comment
string literal # also not a comment
\'\'"string expression
\""\\# word # comment
🫣 Answer
string # not a comment
string literal # also not a comment
\'string expression
"\# word
Bonus: Args All the Way Down
'echo' 'foo' 'bar' 'baz'
Note: this is a property of the shell
2. POSIX Flags
Flags are all arguments that:
- begin with '-'
- until the first argument that doesn't begin with '-'
- or '--'
-flag1
-flag1 --flag2
-flag1 --flag2 arg1
-flag1 --flag2 arg1 -arg2
-flag1 --flag2 -- -arg1
Escaping Flags
touch 'foo' -rf
rm -- -rf
2. GNU Opts
Options are arguments to a flag
foo --bar ./baz
foo --bar=./baz
Be wary...
foo --bar ~/baz
foo --bar=~/baz
3. Arguments, again
The first argument after --
, or not preceded by an option flag.
tar -cf ./etc.tar /etc
Go Flag
import (
"flag"
)
func main() {
name := flag.String("name", "", "the user's real name")
flag.Parse()
// ...
}
Rule #1: There are no Rules
import (
"os"
)
func main() {
if len(os.Args) > 1 {
switch os.Args[1] {
case "-V", "--version", "version":
printVersion()
return
}
}
// ...
}
Rule #2: Avoid pointers
func main() {
var verbose bool
var email string
flag.BoolVar(&verbose, "verbose", false, "show lots of detail")
flag.StringVar(&email, "email", "", "show lots of detail")
}
Rule #3: Create an instance
func main() {
var verbose bool
mainFlags := flag.NewFlagSet("", flag.ContinueOnError)
mainFlags.BoolVar(&verbose, "verbose", false, "show lots of detail")
if err := mainFlags.Parse(); err != nil {
fmt.Println(err)
mainFlags.Usage()
os.Exit(1)
return
}
// ...
}
How to know a flag was set
func isFlagSet(name string) bool {
found := false
flag.Visit(func(f *flag.Flag) {
if f.Name == name {
found = true
}
})
return found
}
END
Q&A