CLI Arguments
Overview
This is a simple way to accept and use command-line arguments in a Go program.
- Use
os.Args
to get arguments os.Args
includes the program name- Slice the arguments to exclude the program name
This helps you write programs that react to inputs like go run main.go hello world
.
Create the Program
To read command-line inputs in Go, use the os
package.
os.Args
returns a slice of strings- First item is the program name
- The rest are your arguments
Here’s a sample program:
package main
import (
"fmt"
"os"
)
func main() {
args := os.Args
fmt.Printf("Hello, World!\n")
fmt.Printf("Arguments: %v\n", args)
}
Run on the terminal:
go run main.go
Expected result:
Hello, World!
Arguments: [/tmp/go-build1001234567/b001/exe/main]
Passing the Argument
Now run the program with the argument first
:
go run main.go first
Output:
Hello, World!
Arguments: [/tmp/go-build1001234567/b001/exe/main first]
We can pass multiple arguments, first
and second
, and it will output both:
Hello, World!
Arguments: [/tmp/go-build1001234567/b001/exe/main first second]
In the output, we can see that it returned three arguments:
/tmp/go-build1001234567/b001/exe/main
first
second
The first argument is always index 0, so the real arguments actually starts from index 1.
Explanation
When you use:
go run main.go first
Go doesn't compile and run main.go
directly in your current directory. Instead, it:
- Compiles it into a temporary binary in a path like
/tmp/go-build.../exe/main
- Then runs that temporary binary with your arguments (in this case,
first
)
Then os.Args
will show:
- The full path to the binary being executed (the first element)
- Followed by any arguments you passed (like
first
)
So the full output is just Go showing:
[<path to temp binary> first]
Build Manually
If you run go build
first:
go build -o myapp
./myapp first
Then os.Args
will look like:
[./myapp first]
Same behavior—just a different binary path.
Accessing Specific Arguments
You can select a specific argument using an index.
- Use
args[1]
for the first real argument - Use a slice like
args[1:3]
to get a range
Modify the previous code so that it only returns the first actual argument which is at index 1.
package main
import (
"fmt"
"os"
)
func main() {
args := os.Args
fmt.Printf("Hello, World!\n")
fmt.Printf("Arguments: %v\n", args[1])
}
Now run the program and pass three arguments: first
, second
, and third
.
go run main.go first second third
Since we only ask for the first item, it will output just the first
:
Hello, World!
Arguments: first
Notice that the output doesn't have square brackets anymore. That's because we're accessing a single element (args[1]
) of the slice, not printing the whole slice.
When we print the entire os.Args
slice, Go shows it with brackets (e.g., [./main first]
). But when we access an individual item, like args[1]
, it prints only the string value of that element with no brackets.
When accessing individual items, make sure that the items exists. Otherwise, you’ll get an error if there aren’t enough arguments.
Slicing Arguments Safely
You can also grab all the arguments passed.
args[1:]
gives all arguments that are passed- No need to hardcode end index
Modify the code again:
package main
import (
"fmt"
"os"
)
func main() {
args := os.Args
fmt.Printf("Hello, World!\n")
fmt.Printf("Arguments: %v\n", args[1:3]) // excluding element at index 3
}
Now run the program and pass the following arguments:
walter
white
jesse
pinkman
saul
goodman
go run main.go walter white jesse pinkman saul goodman
Since we only asked for the first two arguments, it will return:
Hello, World!
Arguments: [walter white]
Out-of-Bounds Errors
If we try to access a slice beyond its available range:
package main
import (
"fmt"
"os"
)
func main() {
args := os.Args
fmt.Printf("Hello, World!\n")
fmt.Printf("Arguments: %v\n", args[1:100])
}
But only provide a few arguments:
go run main.go first second third fourth
We'll get a runtime error like this:
Hello, World!
panic: runtime error: slice bounds out of range [:100] with capacity 5
goroutine 1 [running]:
main.main()
/project/path/main.go:11 +0xc8
exit status 2
The error occurs because os.Args
only contains 5 elements in this case (including the program name), but we're trying to access up to index 99 (args[1:100]
), which exceeds the slice's bounds and causes a panic at runtime.
Preventing Index Errors
Accessing beyond the available number of arguments can cause runtime errors. To avoid them:
- Use
len(args)
to determine how many arguments are available - Avoid accessing indexes outside the valid range of the slice
Example using len()
to safely access arguments:
package main
import (
"fmt"
"os"
)
func main() {
args := os.Args
fmt.Printf("Hello, World!\n")
fmt.Printf("Arguments: %v\n", args[1:len(args)])
}
Run the program with these arguments: ted
, robin
, marshall
, lily
:
go run main.go ted robin marshall lily
The output will display all the provided inputs:
Hello, World!
Arguments: [ted robin marshall lily]
Passing Arguments in VS Code
To test arguments in VS Code, add them in the launch config.
-
Initialize the module first.
go mod init cli ## any name works
-
Click Run --> Add configuration
-
Select
Go: Launch Package
-
This will create a
.vscode/launch.json
-
Add
args
to your configurationExample setup:
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Go Program",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"args": ["first", "second", "third", "fourth"]
}
]
} -
Save the file.
-
Click Run --> Run Without Debugging
-
In the Debug Console, you should see:
Starting: C:\Users\user1\go\bin\dlv.exe dap --listen=127.0.0.1:55953 from C:\project\path
DAP server listening at: 127.0.0.1:55953
Hello, World!
Arguments: [first second third fourth]
Process 28268 has exited with status 0