Golang prompt and interactive CLI: stdin, passwords, promptui, survey

Golang interactive CLI and golang prompt for input with bufio; golang.org/x/term ReadPassword with os.Stdin.Fd; promptui golang and survey; go-prompt (c-bata); notes on TTYs and cross-platform terminals.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Golang prompt and interactive CLI: stdin, passwords, promptui, survey

A golang interactive cli usually combines flags, environment variables (see where to set environment variables on Linux), and a golang prompt for input at runtime. This page walks through a minimal go prompt with bufio, a golang.org/x/term ReadPassword example (the common web search is term.ReadPassword(int(syscall.Stdin)), but int(os.Stdin.Fd()) is the clearer, portable spell), then promptui golang / go promptui for validation and selects, survey for multi-question flows, and the readline-style library c-bata/go-prompt when you need completions. For more stdin-only patterns, see go-prompt on this site.

Checked with Go 1.24 on 64-bit Linux (Fedora and Ubuntu family). Password echo requires a real terminal.


Golang prompt for input with the standard library

Line-based reading from stdin

bufio.Reader.ReadString stops at a delimiter such as newline and is enough for many CLIs:

go
package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func inputPrompt(label string) (string, error) {
	fmt.Fprint(os.Stderr, label+" ")
	s, err := bufio.NewReader(os.Stdin).ReadString('\n')
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(s), nil
}

func main() {
	s, err := inputPrompt("Enter your message:")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	fmt.Printf("Your message: %s\n", s)
}

Run it locally in a terminal; type a line and press Enter. The program echoes your trimmed text on stdout.


golang.org/x/term ReadPassword for hidden input

term.ReadPassword disables local echo while the user types. Install the module in your own project:

bash
go get golang.org/x/term@latest

Use the file descriptor from os.Stdin rather than hard-coding syscall.Stdin (some search snippets write term.ReadPassword(int(syscall.Stdin)); on Linux that is often still fd 0, but os.Stdin.Fd() matches official examples and behaves correctly when stdin is a terminal):

go
package main

import (
	"fmt"
	"os"

	"golang.org/x/term"
)

func readSecret(label string) (string, error) {
	fmt.Fprint(os.Stderr, label+" ")
	pw, err := term.ReadPassword(int(os.Stdin.Fd()))
	if err != nil {
		return "", err
	}
	fmt.Fprintln(os.Stderr)
	return string(pw), nil
}

func main() {
	s, err := readSecret("Password:")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	fmt.Printf("Length: %d\n", len(s))
}

Run in a real TTY; you should see the label on stderr, no visible characters while typing, then a line reporting the password length. In pipes or non-interactive jobs, expect an error instead of silent failure.


promptui golang: validation and select menus

promptui (module path github.com/manifoldco/promptui) covers labeled prompts, optional validation, and arrow-key selects—common when people search golang promptui or go promptui.

bash
go get github.com/manifoldco/promptui@v0.9.0

Validated float input:

go
package main

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/manifoldco/promptui"
)

func main() {
	validate := func(input string) error {
		if _, err := strconv.ParseFloat(input, 64); err != nil {
			return errors.New("enter a float")
		}
		return nil
	}
	p := promptui.Prompt{Label: "Enter a float", Validate: validate}
	out, err := p.Run()
	if err != nil {
		fmt.Println("cancelled:", err)
		return
	}
	fmt.Println("got:", out)
}

Interactive select:

go
package main

import (
	"fmt"

	"github.com/manifoldco/promptui"
)

func main() {
	sel := promptui.Select{
		Label: "Pick one",
		Items: []string{"alpha", "beta", "gamma"},
	}
	_, choice, err := sel.Run()
	if err != nil {
		fmt.Println("cancelled:", err)
		return
	}
	fmt.Println("picked:", choice)
}

Survey for multi-question CLIs

survey chains inputs and multi-selects. Use github.com/AlecAivazis/survey/v2. For MultiSelect, Default must be a []string or []int, not a bare string.

bash
go get github.com/AlecAivazis/survey/v2@v2.3.7
go
package main

import (
	"fmt"
	"strings"

	"github.com/AlecAivazis/survey/v2"
)

func main() {
	qs := []*survey.Question{
		{Name: "name", Prompt: &survey.Input{Message: "Name?"}, Validate: survey.Required},
		{
			Name: "pet",
			Prompt: &survey.MultiSelect{
				Message: "Pets:",
				Options: []string{"dogs", "cats", "birds"},
				Default: []string{"dogs"},
			},
		},
	}
	var ans struct {
		Name string
		Pet  []string `survey:"pet"`
	}
	if err := survey.Ask(qs, &ans); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%s likes %s.\n", ans.Name, strings.Join(ans.Pet, ", "))
}

go-prompt (c-bata) for completion-heavy CLIs

When you need a REPL with suggestions and not just a one-off question, github.com/c-bata/go-prompt is the project many users mean by go-prompt. It is heavier than promptui; pair it with your own command routing.


Summary

Building a golang interactive cli starts with stdin and flags, then layers golang prompt UX: golang.org/x/term ReadPassword with int(os.Stdin.Fd()) for hidden secrets (not pipes), promptui golang for quick validated prompts and selects, survey for multi-step forms, and c-bata go-prompt when completions drive the experience. Match the library to the interaction model, and always handle non-TTY environments where interactive prompts cannot run.


References


Frequently Asked Questions

1. Should I call term.ReadPassword(int(syscall.Stdin))?

Prefer int(os.Stdin.Fd()). It is the documented pattern in golang.org/x/term examples, tracks redirected stdin correctly, and avoids confusion with syscall.Stdin naming. On Unix the numeric fd is usually 0, but using the file descriptor from os.Stdin is clearer.

2. Why does golang.org/x/term ReadPassword fail in my IDE or CI log?

ReadPassword needs a real terminal (TTY) with echo control. Pipes, non-interactive runners, and some integrated terminals return an error such as inappropriate ioctl for device—fall back to normal line reading or skip the prompt in non-TTY environments.

3. What is the difference between promptui golang and go-prompt?

github.com/manifoldco/promptui focuses on labeled prompts, validation, and simple selects. github.com/c-bata/go-prompt is a readline-style library with completions and multiline editing; pick based on whether you need forms versus a shell-like input line.

4. When do I need a third-party package for a golang interactive cli?

Use the standard library for flags, env vars, and line input. Add golang.org/x/term for hidden passwords, promptui or survey for richer menus and validation, or c-bata go-prompt when you need completion-driven REPLs.
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 …