Golang prompt for input: interactive CLI with bufio and fmt

Golang prompt for input and go interactive cli: read lines from stdin with bufio for a go prompt, yes/no prompts, Scanln loops, and validation with strconv; note c-bata go-prompt library vs stdlib for golang prompt and interactive cli golang.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Golang prompt for input: interactive CLI with bufio and fmt

Searches like golang interactive cli, golang prompt for input, go interactive cli, or simply go prompt usually mean one of two things: line-at-a-time stdin prompts with the standard library (bufio, fmt, os), or the third-party line editor module often called go-prompt (github.com/c-bata/go-prompt, sometimes typed go.prompt or go/prompt). This page shows the stdlib approach so you can ship an interactive CLI golang tool without extra dependencies; at the end we point to Survey and go-prompt when you need menus, multi-select, or rich TUI behavior.

Tested with Go 1.24 on Linux.


Single-line golang prompt (stdin)

Standard input (stdin) is the stream your process reads when the user types in the terminal. Read until a newline (newline), trim spaces with strings.TrimSpace, and print the label on os.Stderr so it still shows if os.Stdout is redirected.

go
package main

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

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

func main() {
	name := namePrompt("Enter your name:")
	fmt.Printf("Hello %s, welcome to GoLinuxCloud!\n", name)
}

Run it locally (go run .) and type a name at the prompt; you should see a greeting with the same text. {run=false} avoids the site Run button here because stdin is not available in that environment the same way as on your machine.


Multiple questions and yes/no (go prompt patterns)

Reuse the same reader pattern for several fields. For yes/no, loop until the answer is recognized instead of treating unknown input as “no”.

go
package main

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

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

func yesNoPrompt(label string) bool {
	r := bufio.NewReader(os.Stdin)
	for {
		fmt.Fprintf(os.Stderr, "%s (y/n) ", label)
		s, err := r.ReadString('\n')
		if err != nil {
			return false
		}
		switch strings.ToLower(strings.TrimSpace(s)) {
		case "y", "yes":
			return true
		case "n", "no":
			return false
		}
		fmt.Fprintln(os.Stderr, "Please answer y or n.")
	}
}

func main() {
	name := linePrompt("Enter your name:")
	nationality := linePrompt("Enter your nationality:")
	color := linePrompt("Enter your favourite color:")
	pet := linePrompt("Enter your favourite pet:")
	fmt.Printf("Hello %s from %s. Favourite color %s, pet %s.\n", name, nationality, color, pet)

	if yesNoPrompt("Is this tutorial helpful?") {
		fmt.Println("Thank you!")
	} else {
		fmt.Println("Thanks for the feedback.")
	}
}

Run locally; you should be able to walk through each question and get a coherent summary plus the follow-up branch.


Loop with numeric guess (fmt.Scanln)

For quick numeric input, fmt.Scanln scans typed tokens into variables; it stops at newline. In modern Go the global random source is seeded automatically, so use rand.Intn(100) + 1 for a secret from 1 to 100 inclusive (the old rand.Intn(1, 100) call is not valid Go).

go
package main

import (
	"fmt"
	"math/rand"
)

func main() {
	secret := rand.Intn(100) + 1
	var guess int
	fmt.Println("Guess a number between 1 and 100")
	for {
		if _, err := fmt.Scanln(&guess); err != nil {
			fmt.Println("Please enter a number.")
			continue
		}
		if guess == secret {
			fmt.Println("Congratulations, the secret number is:", guess)
			return
		}
		if guess > secret {
			fmt.Println("Guess a smaller number")
		} else {
			fmt.Println("Guess a bigger number")
		}
	}
}

Play a few rounds locally; hints should move toward the secret.


Validate input (why string first, then strconv)

Scanln into an int can turn bad input into 0 without a clear error. Read a line (or token as string), then strconv.Atoi; loop until parsing succeeds. More on conversions: Go cast and parsing.

go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	fmt.Println("Enter a number:")
	for {
		var s string
		if _, err := fmt.Scan(&s); err != nil {
			fmt.Println("Could not read input")
			return
		}
		i, err := strconv.Atoi(s)
		if err != nil {
			fmt.Println("Enter a valid integer")
			continue
		}
		fmt.Println("Your input number is:", strconv.Itoa(i))
		return
	}
}

Try typing letters, then a valid integer; only the integer path should print the confirmation.


When to reach for a library (Survey, go-prompt)

Stdlib prompts are enough for many CLIs. For multi-select, password masking, or polished surveys, AlecAivazis/survey is widely used. For a Vim-like line editor, completions, and async TUI input, the c-bata project (github.com/c-bata/go-prompt) is what people often mean by go-prompt in module form—add it with go get inside your module, not mixed up with stdin line reading above.


Summary

This article covered golang prompt for input patterns that power an interactive CLI golang tool: line prompts with bufio and os.Stderr, multi-field flows, fmt.Scanln loops for numbers (with a correct rand.Intn range), and string-then-strconv validation so bad typing does not silently become zero. That is distinct from the github.com/c-bata/go-prompt library people sometimes label go.prompt or go/prompt; choose stdlib for simplicity and add a dependency when you need richer UX.


References


Frequently Asked Questions

1. How do I implement a golang prompt for input on the terminal?

Use bufio.NewReader(os.Stdin).ReadString with fmt.Fprint to stderr for the label, then strings.TrimSpace on the result; that gives a simple blocking line prompt for an interactive CLI golang program.

2. What is the difference between go prompt the library and a stdin prompt?

This article focuses on the standard library for line input; the popular TUI line editor is the Go module github.com/c-bata/go-prompt (often written go-prompt or go/prompt), which is a separate dependency for shells and completions.

3. Why use stderr for the prompt label?

So the question still appears if stdout is redirected to a file while the user types on the terminal; stdin stays the input stream.

4. How is fmt.Scanln different from ReadString?

Scanln parses space-separated tokens into typed variables and stops at newline; ReadString returns the raw line as a string, which is easier when you want full-line text or manual parsing with strconv.Atoi.

5. How do I validate numeric input from a prompt?

Read a string line, strconv.Atoi or strconv.ParseFloat, and loop on error instead of scanning directly into an int, which can silently mis-parse bad input.
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 …