Golang Command Line Arguments: os.Args, Flags, and Examples

Read command-line arguments in Go with os.Args, use the flag package for named options, validate positional args, parse numbers safely, and understand go run vs a compiled binary.

Published

Updated

Read time 6 min read

Reviewed byDeepak Prasad

Golang Command Line Arguments: os.Args, Flags, and Examples

This guide is for Go beginners who need to read golang command line arguments from the shell: raw os.Args, safe indexing, the flag package for named options, parsing strings into numbers, and how go run forwards arguments. For iterating argument slices, see iterate over array or slice.

Tested with Go 1.24 on Linux.


Quick answer: os.Args and flags

Command-line arguments in Go are available as the string slice os.Args from the os package. os.Args[0] is the program name or path; os.Args[1:] holds everything the user typed after that. For named options such as -port 8080, use the standard library flag package instead of hand-parsing os.Args.


What are command line arguments?

When you run go run . read write or ./mybin --help, the shell splits the line into tokens. The Go runtime copies them into os.Args. Your program does not receive typed numbers or booleans—only strings—so you convert with strconv or flag helpers when needed.


Read arguments with os.Args

Args is declared in package os as var Args []string. The first element is always present and identifies how the program was invoked; additional elements depend on what the user passed.

go
package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("full slice:", os.Args)
	fmt.Println("program (Args[0]):", os.Args[0])
	if len(os.Args) > 1 {
		fmt.Println("user args (Args[1:]):", os.Args[1:])
	} else {
		fmt.Println("no user arguments (only program name)")
	}
}
Output

In the in-browser Run environment you may see only the program path; on your machine, go run . foo bar adds foo and bar to os.Args[1] and os.Args[2].

Always check len(os.Args) before using os.Args[1], os.Args[2], and so on—otherwise a user who runs the binary with no extras triggers an index-out-of-range panic.


go run vs a compiled binary

How you run What appears in os.Args[0]
go run . a b Compiler-generated temp path or similar; a, b still land in os.Args[1:].
./mybin a b Path you used to invoke ./mybin; a, b in os.Args[1:].

Arguments always belong to your program token list; they are not “inside” the go run subcommand once you place them after the package or file list, for example go run . -- -config when you need a literal -- passed through—use -- after go run options if your tool must see -- itself (rare for small programs).


Positional arguments and validation

Positional args are the non-flag tokens in order: file paths, subcommand names, or IDs. Validate len(os.Args) against the minimum you require and print a short usage line when the count is wrong.

go
package main

import (
	"fmt"
	"os"
)

func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "usage: %s <filename>\n", os.Args[0])
		os.Exit(1)
	}
	file := os.Args[1]
	fmt.Println("would open:", file)
}
Output

Run locally with go run . myfile.txt; without an extra argument it should print usage: to stderr and exit with code 1. The Run button may only show the usage path when no file is passed.


Command line flags with the flag package

Use flag for named options (-name value), defaults, and built-in -h style help. Call flag.Parse() after declaring flags, then read pointer values or use flag.Args() for remaining positional tokens.

go
package main

import (
	"flag"
	"fmt"
)

func main() {
	name := flag.String("name", "guest", "who to greet")
	verbose := flag.Bool("v", false, "verbose output")
	port := flag.Int("port", 8080, "listen port")
	flag.Parse()

	fmt.Printf("hello %s on port %d\n", *name, *port)
	if *verbose {
		fmt.Println("verbose enabled")
	}
	if flag.NArg() > 0 {
		fmt.Println("extra positional:", flag.Args())
	}
}
Output

You can try Run with no arguments (defaults apply) or imagine go run . -name=Ada -v extra.txt where extra.txt becomes a positional argument in flag.Args() after Parse.


Parsing numbers and booleans from strings

Raw os.Args entries are strings. Use strconv.Atoi, ParseBool, or flag.Int / flag.Bool so invalid input returns an error instead of silently mis-parsing.

go
package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	if len(os.Args) != 2 {
		fmt.Println("usage: program <integer>")
		return
	}
	n, err := strconv.Atoi(os.Args[1])
	if err != nil {
		fmt.Println("invalid integer:", err)
		return
	}
	fmt.Println("double:", n*2)
}
Output

Run locally as go run . 21 to see double: 42; with go run . x you should see an invalid integer message.


os.Args vs flag: when to use which

Need Approach
Quick script, one or two positional values os.Args + length checks
Named options, defaults, usage text flag
Mix of both flag.Parse() then flag.Args() for positionals
Many subcommands and nested flags Consider Cobra or another library

Keep simple CLIs on the standard library until complexity grows.


Real-world shapes (brief)

CLI shape Pattern
Input file path First positional after flags, or -f path
Environment name flag.String("env", "dev", ...)
Port flag.Int("port", 8080, ...)
Verbose flag.Bool("v", false, ...)
Subcommand style First positional os.Args[1] as command name, or use Cobra

When to use Cobra or another CLI library

The flag package fits single-command tools and a handful of options.

For example, these are good candidates for the standard flag package:

text
backup --src /data --dest /mnt/backup
server --port 8080 --debug
convert --input app.log --output app.json

These commands have one main action and a small set of options.

A CLI library such as Cobra, urfave/cli, or Kong becomes more useful when your tool grows into many commands and subcommands:

text
mytool db migrate
mytool db rollback
mytool user create
mytool user delete
mytool config set
mytool config get

At that point, you may want:

Requirement Better fit
One command with few flags flag package
Many subcommands Cobra or similar library
Shared flags across commands Cobra or similar library
Generated help for many commands Cobra or similar library
Shell completion support Cobra, urfave/cli v2, Kong, or similar

Short rule:

text
Start with flag for small tools.
Move to a CLI library only when subcommands, shared flags, and help text across many commands justify the extra structure.

Mistakes to avoid

Treating os.Args[1] as the “first argument” without checking length—panics on empty user args.

Forgetting that os.Args[0] is the program path, not user data.

Re-implementing -flag value parsing by hand when flag would be clearer and safer.

Assuming shell quoting is removed differently than it is—the Go program already receives the post-shell token list.

Using os.Args indices for flags that flag already parsed—prefer flag values after Parse.


Go command line arguments cheat sheet

Goal Approach
Raw tokens os.Args
Skip program name os.Args[1:]
Safe indexing if len(os.Args) > n { ... }
Named option flag.String, flag.Int, flag.Bool, etc.
Defaults and -h flag package
Positionals after flags flag.Args() after flag.Parse()
String to number strconv.Atoi / ParseInt with errors
go run with args go run . arg1 arg2
Rich subcommands Cobra or similar

Summary

Golang command line arguments live in os.Args: index 0 is the program invocation, 1: are user tokens. Check length before indexing, convert types explicitly, and use the flag package when you want named options, defaults, and usage text. go run . forwards trailing tokens to your program the same way a compiled binary does. For large multi-command CLIs, graduate from flag to a dedicated library after the complexity is real.


References


Frequently Asked Questions

1. How do I read command-line arguments in Go?

Use os.Args, a []string where index 0 is the program name or path and os.Args[1:] are user arguments; for named flags use the standard library flag package.

2. What is the difference between os.Args and the flag package?

os.Args is the raw argument slice; flag parses -name value style options, defaults, and usage text for you.

3. Why does my program panic when reading os.Args[1]?

If the user passes no extra arguments, len(os.Args) is 1 and indexing past that panics—check length before indexing.

4. How do I pass arguments to go run?

Put them after the package or files: go run . arg1 arg2 — everything after the package list becomes os.Args[1:] for your program.

5. When should I use Cobra instead of flag?

Use flag for small CLIs; reach for Cobra or similar when you need subcommands, nested flags, and generated help at scale.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …