Command-Line Arguments in Golang
Command-line arguments allow you to pass data to your Go programs when you run them from the terminal. Go provides several ways to handle command-line arguments, from simple access to the raw arguments to more sophisticated flag parsing.
Basic Command-Line Arguments
The simplest way to access command-line arguments is through the os.Args variable.
package main
import (
"fmt"
"os"
)
func main() {
// os.Args[0] is the program name
fmt.Println("Program name:", os.Args[0])
// os.Args[1:] contains the actual arguments
fmt.Println("Arguments:", os.Args[1:])
// Number of arguments
fmt.Println("Number of arguments:", len(os.Args)-1)
// Process each argument
for i, arg := range os.Args[1:] {
fmt.Printf("Argument %d: %s\n", i+1, arg)
}
}Run this program:
go run basic-args.go hello world 123Output:
Program name: /tmp/go-build123456789/b001/exe/basic-args
Arguments: [hello world 123]
Number of arguments: 3
Argument 1: hello
Argument 2: world
Argument 3: 123Using the flag Package
The flag package provides a more sophisticated way to parse command-line flags.
package main
import (
"flag"
"fmt"
)
func main() {
// Define flags
var name string
var age int
var verbose bool
// String flag
flag.StringVar(&name, "name", "Anonymous", "Your name")
// Int flag
flag.IntVar(&age, "age", 0, "Your age")
// Bool flag
flag.BoolVar(&verbose, "verbose", false, "Verbose output")
// Parse the flags
flag.Parse()
// Access flag values
fmt.Printf("Name: %s\n", name)
fmt.Printf("Age: %d\n", age)
fmt.Printf("Verbose: %t\n", verbose)
// Remaining arguments after flags
fmt.Println("Remaining arguments:", flag.Args())
}Usage examples:
go run flag-example.go -name "John Doe" -age 30 -verbose
go run flag-example.go -name Alice extra arg1 arg2Different Flag Types
The flag package supports various data types:
package main
import (
"flag"
"fmt"
"time"
)
func main() {
// String flag
var message = flag.String("message", "Hello", "a message to print")
// Int flag
var count = flag.Int("count", 1, "number of times to print")
// Int64 flag
var bigNumber = flag.Int64("bignum", 1000, "a big number")
// Float64 flag
var ratio = flag.Float64("ratio", 1.0, "a ratio value")
// Bool flag
var debug = flag.Bool("debug", false, "enable debug mode")
// Duration flag
var timeout = flag.Duration("timeout", 5*time.Second, "timeout duration")
// Custom flag (using flag.Var)
var customFlag customType
flag.Var(&customFlag, "custom", "custom flag value")
flag.Parse()
fmt.Printf("Message: %s\n", *message)
fmt.Printf("Count: %d\n", *count)
fmt.Printf("Big Number: %d\n", *bigNumber)
fmt.Printf("Ratio: %.2f\n", *ratio)
fmt.Printf("Debug: %t\n", *debug)
fmt.Printf("Timeout: %v\n", *timeout)
fmt.Printf("Custom: %s\n", customFlag.value)
}
// Custom type for flag
type customType struct {
value string
}
func (c *customType) String() string {
return c.value
}
func (c *customType) Set(value string) error {
c.value = "custom:" + value
return nil
}Flag Shortcuts
You can create shorter flag names using the same variable:
package main
import (
"flag"
"fmt"
)
func main() {
// Long and short versions of the same flag
var output = flag.String("output", "output.txt", "output file")
var o = flag.String("o", "output.txt", "output file (short)")
flag.Parse()
// Use the long version, but both will work
fmt.Printf("Output file: %s\n", *output)
fmt.Printf("Short output: %s\n", *o)
}Subcommands
For programs with subcommands, you can handle them manually:
package main
import (
"flag"
"fmt"
"os"
)
func main() {
// Check if we have subcommands
if len(os.Args) < 2 {
fmt.Println("Usage: program <command> [arguments]")
fmt.Println("Commands: create, delete, list")
os.Exit(1)
}
subcommand := os.Args[1]
switch subcommand {
case "create":
createCommand(os.Args[2:])
case "delete":
deleteCommand(os.Args[2:])
case "list":
listCommand(os.Args[2:])
default:
fmt.Printf("Unknown command: %s\n", subcommand)
os.Exit(1)
}
}
func createCommand(args []string) {
fs := flag.NewFlagSet("create", flag.ExitOnError)
name := fs.String("name", "", "name of item to create (required)")
size := fs.Int("size", 100, "size of item")
fs.Parse(args)
if *name == "" {
fs.Usage()
os.Exit(1)
}
fmt.Printf("Creating item '%s' with size %d\n", *name, *size)
}
func deleteCommand(args []string) {
fs := flag.NewFlagSet("delete", flag.ExitOnError)
name := fs.String("name", "", "name of item to delete (required)")
force := fs.Bool("force", false, "force deletion")
fs.Parse(args)
if *name == "" {
fs.Usage()
os.Exit(1)
}
if *force {
fmt.Printf("Force deleting item '%s'\n", *name)
} else {
fmt.Printf("Deleting item '%s'\n", *name)
}
}
func listCommand(args []string) {
fs := flag.NewFlagSet("list", flag.ExitOnError)
format := fs.String("format", "table", "output format (table, json, csv)")
limit := fs.Int("limit", 10, "maximum number of items to list")
fs.Parse(args)
fmt.Printf("Listing items (format: %s, limit: %d)\n", *format, *limit)
}Usage:
go run subcommands.go create -name "myitem" -size 200
go run subcommands.go delete -name "myitem" -force
go run subcommands.go list -format json -limit 50Environment Variables
Sometimes you want to accept configuration from environment variables as well as flags:
package main
import (
"flag"
"fmt"
"os"
)
func main() {
// Define flags with default values
var port = flag.Int("port", 8080, "server port")
var host = flag.String("host", "localhost", "server host")
var debug = flag.Bool("debug", false, "enable debug mode")
// Override defaults with environment variables if set
if envPort := os.Getenv("PORT"); envPort != "" {
if p, err := parseInt(envPort); err == nil {
*port = p
}
}
if envHost := os.Getenv("HOST"); envHost != "" {
*host = envHost
}
if envDebug := os.Getenv("DEBUG"); envDebug == "true" {
*debug = true
}
flag.Parse()
fmt.Printf("Server will run on %s:%d (debug: %t)\n", *host, *port, *debug)
}
// Simple integer parser (you'd use strconv.Atoi in real code)
func parseInt(s string) (int, error) {
var result int
for _, r := range s {
if r < '0' || r > '9' {
return 0, fmt.Errorf("invalid integer")
}
result = result*10 + int(r-'0')
}
return result, nil
}Configuration File Support
For more complex configuration, you might want to read from a config file:
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
)
type Config struct {
Port int `json:"port"`
Host string `json:"host"`
Database struct {
Host string `json:"host"`
Port int `json:"port"`
Name string `json:"name"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"database"`
}
func main() {
var configFile = flag.String("config", "config.json", "configuration file")
flag.Parse()
config, err := loadConfig(*configFile)
if err != nil {
fmt.Printf("Error loading config: %v\n", err)
os.Exit(1)
}
fmt.Printf("Server config: %s:%d\n", config.Host, config.Port)
fmt.Printf("Database: %s:%d/%s\n", config.Database.Host, config.Database.Port, config.Database.Name)
}
func loadConfig(filename string) (*Config, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
var config Config
decoder := json.NewDecoder(file)
err = decoder.Decode(&config)
if err != nil {
return nil, err
}
return &config, nil
}Example config.json:
{
"port": 8080,
"host": "0.0.0.0",
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp",
"username": "user",
"password": "password"
}
}Best Practices
- Use meaningful flag names - Choose descriptive names for your flags
- Provide good help text - Use clear descriptions for each flag
- Handle required flags - Check for required flags and show usage if missing
- Use consistent naming - Follow conventions (kebab-case for flags, camelCase for variables)
- Support environment variables - Allow configuration via environment for containerized apps
- Validate input - Check flag values for validity
- Show usage on error - Use flag.Usage() to display help when flags are invalid
Advanced: Custom Flag Types
You can create custom flag types by implementing the flag.Value interface:
package main
import (
"flag"
"fmt"
"strconv"
"strings"
)
// IntList represents a list of integers
type IntList []int
func (il *IntList) String() string {
return fmt.Sprintf("%v", *il)
}
func (il *IntList) Set(value string) error {
// Parse comma-separated values
parts := strings.Split(value, ",")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
num, err := strconv.Atoi(part)
if err != nil {
return err
}
*il = append(*il, num)
}
return nil
}
func main() {
var numbers IntList
flag.Var(&numbers, "numbers", "comma-separated list of numbers")
flag.Parse()
fmt.Printf("Numbers: %v\n", numbers)
// Calculate sum
sum := 0
for _, n := range numbers {
sum += n
}
fmt.Printf("Sum: %d\n", sum)
}Usage:
go run custom-flag.go -numbers "1,2,3,4,5"External Resources:
Related Tutorials: