- Home
- >
- Software Development
- >
- Command Line Programming with Go – InApps Technology 2022
Command Line Programming with Go – InApps Technology is an article under the topic Software Development Many of you are most interested in today !! Today, let’s InApps.net learn Command Line Programming with Go – InApps Technology in today’s post !
Read more about Command Line Programming with Go – InApps Technology at Wikipedia
You can find content about Command Line Programming with Go – InApps Technology from the Wikipedia website
CLI, or “command line interface,” is a program that users interact with on the command line. Go has become a very popular choice for CLI development due to its lack of deployment dependencies by compiling to a static binary. If you have written a CLI that has dependencies for installation, you know how important this can be.
In this post, we will cover the basics of creating a CLI with Go and the standard library packages required to do so.
Arguments
Most CLI programs expect some input in the form of CLI arguments. Program arguments are handled in Go as a slice of strings:
Retrieving the name of the currently running program
package main
import ( “fmt” “os” )
func main() { // Program Name is always the first (implicit) argument cmd := os.Args[0]
fmt.Printf(“Program Name: %sn”, cmd) } |
This program is in the code/example1 directory. You can build and run it with these commands:
You should see the following output:
Determine the Number of Arguments Passed into Your Program
To find out how many arguments were passed in, you can take the length of arguments and subtract 1 (remember that the first argument is always your program name). Or, you can augment the slice by using os.Args[1:] which simply says: “give me a new subslice starting with index 1 (not 0) to the end of the slice.”
package main
import ( “fmt” “os” )
func main() { argCount := len(os.Args[1:]) fmt.Printf(“Total Arguments (excluding program name): %dn”, argCount) } |
Running this with ./example2 will result in Total Arguments (excluding program name): 0
Running with this ./example2 –foo=bar will result in Total Arguments (excluding program name): 1
Enumerating arguments
Here is a quick way to run through the current arguments
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package main
import ( “fmt” “os” )
func main() { for i, a := range os.Args[1:] { fmt.Printf(“Argument %d is %sn”, i+1, a) }
} Running the program with ./example3 –local u=admin —help results in: Argument 1 is –local Argument 2 is u=admin Argument 3 is —help |
The Flag package
So far we have examined how to look at the arguments of a program at a very basic level. Interrogating them at this level and assigning them to variables for use in our program would be very cumbersome. This is where the Flag Package comes in.
package main
import ( “flag” “fmt” )
func main() { var port int flag.IntVar(&port, “p”, 8000, “specify port to use. defaults to 8000.”) flag.Parse()
fmt.Printf(“port = %d”, port) } |
The first thing we did is state that we are going to have a flag that is of type int with a default value of 8000, and usage text to help users understand our intent for that flag.
To have the flags package populate your defined flags, you need to call flag.Parse().
Running this program, with no arguments, will result in port = 8000. This is because we told the flag package that if nothing is passed in for port, the default of 8000 should be used.
Running with ./example4 –p=9000 results in port = 9000.
As a bonus, the flag package provides us with “program usage” output for free. If we run ./example4 –help we now get:
Usage of ./example4:
-p=8000: specify port to use. defaults to 8000.
flag.Args()
Many CLI programs take both flags and “non-flag” arguments. flag.Args() will return the arguments that it does not consider flags.
package main
import ( “flag” “fmt” )
func main() { var port int flag.IntVar(&port, “p”, 8000, “specify port to use. defaults to 8000.”) flag.Parse()
fmt.Printf(“port = %dn”, port) fmt.Printf(“other args: %+vn”, flag.Args()) } |
Running this program with ./example5 -p=9000 foo=10 -bar will result in:
port = 9000
other args: [foo=10 -bar]
The flag package will stop looking for flags as soon as it encounters any parameter that doesn’t begin with a hyphen. Because foo=10 isn’t a hyphen-prefixed flag, it, and everything after it (including -bar) will be considered a non-option flag.
This distinction allows command [flags] subcommand [subflags] style applications to be built with ease.
Invalid flag arguments
Go is a strongly typed language, so if we try to pass an arbitrary string into an int flag, it will let us know.
package main
import ( “flag” “fmt” )
func main() { var port int flag.IntVar(&port, “p”, 8000, “specify port to use. defaults to 8000”) flag.Parse()
fmt.Printf(“port = %d”, port) } |
Running this program with ./example6 -p=foo results in:
invalid value “foo” for flag -p: strconv.ParseInt: parsing “foo”: invalid syntax
Usage of ./example6:
-p=8000: specify port to use. defaults to 8000
Not only does the flag package tell us what we did wrong, but it also prints out the default usage as well.
flag.Usage
The flag package publicly declares a Usage function variable, which allows us to re-assign it to any function we want, thus allowing us to have custom usage output.
package main
import ( “flag” “fmt” “os” )
func main() { flag.Usage = func() { fmt.Printf(“Usage of %s:n”, os.Args[0]) fmt.Printf(” example7 file1 file2 …n”) flag.PrintDefaults() } flag.Parse() } |
Running this program with ./example7 –help results in:
Usage of ./example7:
example7 file1 file2 …
Getting input
Up until now, we have been outputting from our CLI but not accepting input once the program starts. We can do some basic input capturing via stdin with fmt.Scanf.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package main
import “fmt”
func main() { var guessColor string const favColor = “blue” for { fmt.Println(“Guess my favorite color:”) if _, err := fmt.Scanf(“%s”, &guessColor); err != nil { fmt.Printf(“%sn”, err) return } if favColor == guessColor { fmt.Printf(“%q is my favorite color!”, favColor) return } fmt.Printf(“Sorry, %q is not my favorite color. Guess again.n”, guessColor) } } |
bufio.Scanner
While fmt.Scanf is great for getting some simple input, we many times require an entire line at a time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package main
import ( “bufio” “fmt” “os” )
func main() { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { line := scanner.Text() if line == “exit” { os.Exit(0) } fmt.Println(line) // Println will add back the final ‘n’ } if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, “reading standard input:”, err) } } |
This is a basic echo program. To exit the program, type exit.
A basic cat program
Many of you have used cat. We are going to put together much of what we have learned in this post and build a very basic version of cat.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package main
import ( “flag” “fmt” “io” “os” )
func main() { flag.Usage = func() { fmt.Printf(“Usage of %s:n”, os.Args[0]) fmt.Printf(” cat file1 file2 …n”) flag.PrintDefaults() }
flag.Parse() if flag.NArg() == 0 { flag.Usage() os.Exit(1) }
for _, fn := range flag.Args() { f, err := os.Open(fn); if err != nil { panic(err) } _, err = io.Copy(os.Stdout, f) if err != nil { panic(err) } } } |
Help!
As we have already covered above, help parameters are implicitly defined if you don’t explicitly define them:
-h
–h
-help
–help
The above will all trigger usage and, depending on the how you initialize the flag parser, may cause your package to exit with an error. Also notice long and short forms of arguments are treated the same in the flag package.
Summary
As you can see, there are a lot of options when building a CLI with Go. We have only covered the basics in this post. I would suggest a read of the following packages for a more in depth knowledge:
Additional command line libraries
There are several third party libraries that assist in making cli easier. I would recommend looking at them to see if they serve your needs as well.
Cory LaNou is an experienced software developer with over two decades of experience, and two and a half years of production Go experience. He is currently a lead instructor at gSchool where he teaches Go and leads the local Denver Go meetup, aptly called Denver Gophers
Feature image via Flickr Creative Commons.
Source: InApps.net
Let’s create the next big thing together!
Coming together is a beginning. Keeping together is progress. Working together is success.