We often use CLI tools and commands like bash
, awk
, sed
, and many others for both business and play. Additionally, we enjoy creating CLI apps, therefore in this post, we'll demonstrate how to add various interactive prompts to your Go-based CLI apps. Sometimes, we want to pass data to CLI apps. It is relatively common and rather simple to build using just the standard Go library to use flags, environment variables, file names, or reading from standard input. You may spice up your CLI application and enhance the user experience by using interactive prompts.
Simple text input prompt in Golang
In the previous article, we walked through a basic text input prompt. Just read till we reach the next line character (\n
) in standard input:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// InputPrompt receives a string value using the label
func InputPrompt(label string) string {
var s string
r := bufio.NewReader(os.Stdin)
for {
fmt.Fprint(os.Stderr, label+" ")
s, _ = r.ReadString('\n')
if s != "" {
break
}
}
return strings.TrimSpace(s)
}
func main() {
message := InputPrompt("Enter your message:")
fmt.Printf("Your message: %s!\n", message)
}
Output:
Enter your message: Hello GoLinuxCloud members!
Your message: Hello GoLinuxCloud members!!
Hidden password input prompt
Password prompts are similar to input prompts, except the user's written input ought to be hidden. We can use term package to achieve this purpose. First you have to install term package by this command:
go get -u golang.org/x/term
Here is an example of reading password from console using ReadPassword()
function:
func ReadPassword(fd int) ([]byte, error)
: ReadPassword reads a line of input from a terminal without local echo. This is commonly used for inputting passwords and other sensitive data. The slice returned does not include the \n.
package main
import (
"fmt"
"os"
"syscall"
"golang.org/x/term"
)
// The entered password will not be displayed on the screen
func SensitivePrompt(label string) string {
var s string
for {
fmt.Fprint(os.Stderr, label+" ")
pw, _ := term.ReadPassword(int(syscall.Stdin))
s = string(pw)
if s != "" {
break
}
}
fmt.Println()
return s
}
func main() {
sensitiveString := SensitivePrompt("Enter your password:")
fmt.Printf("Your password is %q\n", sensitiveString)
}
Output:
Enter your password:
Your password is "HelloPass0rd"
Perform input validation and formatting with promptui
Promptui
is a library providing a simple interface to create command-line prompts for go. Prompt()
function provides a single line for user input. Prompt supports optional live validation, confirmation and masking the input. In the below example, we will see how to validate the input with Prompt()
function:
package main
import (
"errors"
"fmt"
"strconv"
"github.com/manifoldco/promptui"
)
func main() {
// validate the input
validate := func(input string) error {
_, err := strconv.ParseFloat(input, 64)
if err != nil {
return errors.New("Invalid input, please enter a float")
}
return nil
}
// Each template displays the formatted data received.
templates := &promptui.PromptTemplates{
Prompt: "{{ . }} ",
Valid: "{{ . | blue }} ",
Invalid: "{{ . | yellow }} ",
Success: "{{ . | bold }} ",
}
prompt := promptui.Prompt{
Label: "Enter a float: ",
Templates: templates,
Validate: validate,
}
result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
// print out the result, if valid, error is displayed in a formatted message.
fmt.Printf("Your float is: %s\n", result)
}
Output:
Create interactive CLI prompt with promptui
We'll use an promptui package to create an interactive select prompt:
package main
import (
"fmt"
"github.com/manifoldco/promptui"
)
func main() {
prompt := promptui.Select{
Label: "Select your characters:",
Items: []string{"Harry Potter", "Ron Weasley", "Hermione Granger", "Ginny Weasley", "Neville Longbottom",
"Luna Lovegood", "Draco Malfoy", "Albus Dumbledore"},
}
_, result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You choose %q\n", result)
}
Output:
Multi-Options prompt with survey package
To create an interactive multi-select prompt we're going to use an awesome survey package. Here is an simple example of multi-select prompt:
package main
import (
"fmt"
"strings"
"github.com/AlecAivazis/survey/v2"
)
// list questions to ask
var qs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "Enter your name?"},
Validate: survey.Required,
Transform: survey.Title,
},
{
Name: "pet",
Prompt: &survey.MultiSelect{
Message: "Choose your pets:",
Options: []string{"dogs", "reptiles", "cats", "birds", "fish", "rabbits", "pigs", "rats", "mices"},
Default: "dogs",
},
},
{
Name: "rating",
Prompt: &survey.Input{Message: "Rate our website (integer number):"},
},
}
func main() {
// store answer to struct
answer := struct {
Name string // survey match the question and field names
Pet []string `survey:"pet"` //tag fields to match a specific name
Rating int // if the types don't match, survey will convert it
}{}
err := survey.Ask(qs, &answer)
if err != nil {
fmt.Println(err.Error())
return
}
petString := strings.Join(answer.Pet, ", ")
fmt.Printf("%s likes %s.", answer.Name, petString)
}
Output:
Summary
As you can see, basic interactive prompts are fairly simple to implement, but for more complex ones, some Go packages from the community are recommended: promptui, survey, prompter, term,... In today post, I have illustrate how to use promptui and survey to create some interesting examples with select, multi select options in prompt.
References
https://github.com/manifoldco/promptui
https://pkg.go.dev/golang.org/x/term