How to read user input from the command line in Go

You’re building a CLI program but you want to be able to prompt a user for additional data after the program has already has started as opposed to using command line flags or environmental variables. This article will discuss a few ways to do this in Go.

There are multiple ways to read user input from the terminal console in Go. Let’s consider three basic scenarios you are likely to encounter when developing CLIs in Go.

Read a single line of text

If all you need to do is accept a single word or short sentence from the user, the bufio.Reader.ReadString method may come in handy. Here’s how to use it:

package main

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

func main() {
	fmt.Print("Enter text: ")
	reader := bufio.NewReader(os.Stdin)
	// ReadString will block until the delimiter is entered
	input, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println("An error occured while reading input. Please try again", err)
		return
	}

	// remove the delimeter from the string
	input = strings.TrimSuffix(input, "\n")
	fmt.Println(input)
}

The bufio.NewReader() method takes an argument that implements the io.Reader interface. os.Stdin implements this interface and represents an open file that points to the standard input file descriptor. This method returns a bufio.Reader struct which has a method called ReadString() used above.

The ReadString() method takes in a delimiter (the newline character \n in this case which will be appended if the user presses the Enter key) and reads the user’s input until the first occurrence of the delimiter in the string. After this, two values are returned: a string containing the data up to and including the delimiter, and an error (if any).

After handling the error, you need to remove the delimiter from the user input. The strings.TrimSuffix() method takes care of this nicely leaving us with exactly what the user inputted.

Read multiple lines of text

To read multiple lines of text, use the bufio.NewScanner() method as shown below:

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Print("Enter Text: ")
		// reads user input until \n by default
		scanner.Scan()
		// Holds the string that was scanned
		text := scanner.Text()
		if len(text) != 0 {
			fmt.Println(text)
		} else {
			// exit if user entered an empty string
			break
		}

	}

	// handle error
	if scanner.Err() != nil {
		fmt.Println("Error: ", scanner.Err())
	}
}

The NewScanner() method also takes in an io.Reader and returns a new scanner to read from the reader. Each time scanner.Scan() is called, the program blocks until the user enters a new line of text. By default, the scanner will consume the text until a newline character is reached, but this can be configured using the scanner.Split method.

The text entered the user can be retrieved using scanner.Text(). You can print it out to the screen as we’ve done above, or save it somewhere like a slice of strings. If an empty string is entered, the for loop will exit.

The scanner.Err() method returns the first non-EOF error that was encountered by the scanner. Be sure to handle this error appropriately before proceeding with the rest of your program logic.

If you run the above code, you will get the following output:

$ go run main.go
Enter Text: Spain
Spain
Enter Text: France
France
Enter Text: Germany
Germany
Enter Text:

You can also use this method to read just a single like of text. The code below will suffice for this purpose:

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print("Enter Text: ")
	scanner.Scan()
	fmt.Println(scanner.Text())

	if scanner.Err() != nil {
		fmt.Println("Error: ", scanner.Err())
	}
}

Read a single UTF-8 Encoded Character

If all you need is a single character, you can use the bufio.Reader.ReadRune method. Even if the user enters more than one character, only the first one is considered while all others are ignored.

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	char, _, err := reader.ReadRune()
	if err != nil {
		fmt.Println(err)
	}

	// prints the unicode code point of the character
	fmt.Println(char)
}

Conclusion

As you can see, reading user input from the terminal console can be achieved in multiple ways depending on your needs. The above samples should cover the most common use cases. Feel free to dig deeper into the documentation if what I’ve provided above does not meet your needs.

Thanks for reading, and happy coding!