Question
I want to read standard input from the command line in Go, similar to Console.ReadLine() in C#. However, my program often exits before I can enter anything. What is the correct way to read console input in Go?
Here is my current code:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
fmt.Println(text)
fmt.Println("Enter text: ")
text2 := ""
fmt.Scanln(text2)
fmt.Println(text2)
ln := ""
fmt.Sscanln("%v", ln)
fmt.Println(ln)
}
I am looking for the Go equivalent of reading a full line from standard input, and I also want to understand why the other attempts do not work as expected.
Short Answer
By the end of this page, you will understand how to read input from standard input in Go, when to use bufio.Reader versus fmt.Scanln, and why some input attempts fail. You will also learn beginner-friendly patterns for reading full lines, single values, and handling common input mistakes.
Concept
In Go, reading from standard input means reading data from os.Stdin, which represents what the user types in the terminal.
There are two common approaches:
bufio.Readerfor reading full lines or larger chunks of textfmt.Scan,fmt.Scanln, orfmt.Scanffor scanning space-separated values into variables
If you want behavior similar to C#'s Console.ReadLine(), the closest match is usually reading from os.Stdin with bufio.Reader.
Why this matters
Console input is useful in many kinds of programs:
- small command-line tools
- coding challenge programs
- interactive scripts
- configuration prompts
- debugging utilities
A key detail in Go is that some input functions expect pointers to variables. If you pass a normal value instead of a pointer, the function cannot store the result in your variable.
For example, this is wrong:
text2 := ""
fmt.Scanln(text2)
This passes the value of text2, not its address. Go needs the address so it can write the input into that variable.
Correct version:
text2 := ""
fmt.Scanln(&text2)
Also, fmt.Sscanln does not read from standard input. It scans data from a string you already have in memory. That is why it is not the right tool for reading from the terminal.
Mental Model
Think of standard input as a stream of characters flowing from the keyboard into your program.
os.Stdinis the pipe connected to the keyboard inputbufio.Readeris a helper that lets you collect characters until a stopping point, such as a newlinefmt.Scanlnis more like a form parser: it reads typed values and places them into variables you provide
A simple analogy:
bufio.Reader= reading an entire sentence from a notebook linefmt.Scanln= filling labeled boxes with typed values
If you want the whole line exactly as the user typed it, use a reader. If you want individual values like a name, age, or number, scanning functions can be simpler.
Syntax and Examples
Read a full line with bufio.Reader
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
text, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error:", err)
return
}
text = strings.TrimSpace(text)
fmt.Println("You entered:", text)
}
Explanation
bufio.NewReader(os.Stdin)creates a reader connected to keyboard input.ReadString('\n')keeps reading until the user presses Enter.- The returned text usually includes the newline, so
strings.TrimSpace()is often used to remove it.
Read a single word with fmt.Scanln
package main
import "fmt"
func main() {
var name string
fmt.Print("Enter your name: ")
_, err := fmt.Scanln(&name)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Hello,", name)
}
Explanation
fmt.Scanln(&name)stores input inname&nameis required because the function needs a pointerScanlnis best for simple values, not full sentences with spaces
Modern full-line option with bufio.Scanner
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
fmt.Print("Enter text: ")
if scanner.Scan() {
text := scanner.Text()
fmt.Println("You entered:", text)
}
if err := scanner.Err(); err != nil {
fmt.Println("Error:", err)
}
}
This is often the cleanest way to read one line at a time.
Step by Step Execution
Consider this example:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error:", err)
return
}
input = strings.TrimSpace(input)
fmt.Println("Final value:", input)
}
What happens step by step
-
reader := bufio.NewReader(os.Stdin)- A reader is created that listens to terminal input.
-
fmt.Print("Enter text: ")- The prompt is shown without adding a newline.
-
input, err := reader.ReadString('\n')- The program pauses and waits.
- The user types something like
hello worldand presses Enter. - The value returned is usually
"hello world\n".
-
if err != nil { ... }- If reading fails, the program prints the error and stops.
-
input = strings.TrimSpace(input)- Removes the trailing newline and any surrounding whitespace.
- Now the value is
"hello world".
-
fmt.Println("Final value:", input)- Prints the cleaned result.
Why your fmt.Scanln(text2) version fails
text2 := ""
fmt.Scanln(text2)
Step by step:
text2is an empty string.fmt.Scanln(text2)receives a copy of that empty string.- The function cannot modify the original variable.
- As a result,
text2stays empty.
Correct version:
text2 := ""
fmt.Scanln(&text2)
Now Go receives the address of text2 and can store the input there.
Real World Use Cases
Reading from standard input is common in practical Go programs.
Command-line utilities
A script may ask for a filename, search term, or confirmation:
fmt.Print("Enter filename: ")
Coding challenge platforms
Many competitive programming tasks read data from standard input instead of from files or prompts.
Setup scripts
A tool might ask for environment values:
- database host
- port number
- API key label
Interactive admin tools
Terminal apps often request commands like:
createdeletelist
Data entry programs
You may ask the user to enter names, quantities, or notes, then process those values inside the program.
Real Codebase Usage
In real Go codebases, developers usually choose the input tool based on the shape of the data.
Common patterns
Full-line input for free-form text
Used for:
- search queries
- comments
- prompts with spaces
- file paths
Typical choice:
reader := bufio.NewReader(os.Stdin)
line, err := reader.ReadString('\n')
Token-based input for structured values
Used for:
- menu choices
- numbers
- short identifiers
Typical choice:
var choice int
fmt.Scanln(&choice)
Validation after input
Developers rarely trust raw input directly.
if strings.TrimSpace(name) == "" {
fmt.Println("Name cannot be empty")
return
}
Early returns on input errors
A very common Go style is:
input, err := reader.ReadString('\n')
if err != nil {
return
}
This keeps code simple and readable.
Reusable helper functions
In larger codebases, input logic is often wrapped in functions:
func readLine() (string, error) {
reader := bufio.NewReader(os.Stdin)
return reader.ReadString('\n')
}
This avoids repeating the same setup code everywhere.
Common Mistakes
1. Passing a value instead of a pointer
Broken code:
var name string
fmt.Scanln(name)
Problem:
Scanlncannot write intonamebecause it needs a pointer.
Correct code:
var name string
fmt.Scanln(&name)
2. Using fmt.Sscanln for terminal input
Broken code:
var name string
fmt.Sscanln("%v", name)
Problem:
Sscanlnscans from a string, not from standard input.- It is the wrong function for keyboard input.
Correct approach:
fmt.Scanln(&name)
or
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
3. Ignoring errors
Broken code:
text, _ := reader.ReadString('\n')
Problem:
- If reading fails, you do not know why.
Better:
text, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error:", err)
return
}
4. Expecting Scanln to read full sentences easily
Broken expectation:
var text string
fmt.Scanln(&text)
If the user types hello world, Scanln is not ideal for preserving the entire line as free-form text.
Use:
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
5. Forgetting to remove the newline
With ReadString('\n'), the returned string often ends with \n.
Fix:
text = strings.TrimSpace(text)
6. Mixing multiple input methods carelessly
If you mix Scanln, Scan, and buffered readers without understanding how they consume input, leftover newlines can cause confusing results.
A good beginner rule:
- Prefer one style consistently in a small program
- Use
bufio.Readerorbufio.Scannerfor line-based input - Use
fmt.Scanlnfor simple token-based input
Comparisons
| Approach | Best for | Reads spaces? | Needs pointer? | Notes |
|---|---|---|---|---|
bufio.Reader.ReadString('\n') | Full lines | Yes | No | Closest to Console.ReadLine() |
bufio.Scanner | Line-by-line input | Yes | No | Simple and clean for basic line reading |
fmt.Scan | Space-separated values | No | Yes | Reads tokens until whitespace |
fmt.Scanln | Values on one line | Limited | Yes | Stops at newline; good for simple input |
fmt.Scanf | Formatted input | Depends on format | Yes | Useful when input format is strict |
fmt.Sscanln | Scanning from a string variable | Not for stdin | Yes | Does not read from keyboard |
bufio.Reader vs fmt.Scanln
- Use
bufio.Readerwhen you want the entire line - Use
fmt.Scanlnwhen you want one or more simple values
bufio.Reader vs bufio.Scanner
Readergives more controlScanneris often easier for beginners when reading line by line
If your goal is a Go version of Console.ReadLine(), bufio.Reader or bufio.Scanner is usually the best match.
Cheat Sheet
Full line input
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
Remove newline:
text = strings.TrimSpace(text)
Single value input
var name string
_, err := fmt.Scanln(&name)
Important rule
Scanning functions need pointers:
fmt.Scanln(&value) // correct
fmt.Scanln(value) // wrong
Useful imports
import (
"bufio"
"fmt"
"os"
"strings"
)
Best tool by situation
- Full sentence or line:
bufio.Readerorbufio.Scanner - Single word or number:
fmt.Scanln - Parse from an existing string:
fmt.Sscanln
Common edge cases
ReadString('\n')includes the newlineScanlnis not ideal for long text with spaces- Always check
errwhen reading input - Mixing input styles can leave extra newlines behind
FAQ
What is the Go equivalent of Console.ReadLine()?
The closest equivalent is usually reading from os.Stdin using bufio.Reader or bufio.Scanner.
Why does fmt.Scanln(text2) not work?
Because Scanln needs a pointer. Use fmt.Scanln(&text2) instead.
Should I use bufio.Reader or bufio.Scanner?
Use bufio.Scanner for simple line-by-line input. Use bufio.Reader when you want more control.
Does fmt.Sscanln read from the keyboard?
No. It reads from a string already in memory, not from standard input.
Why does my input contain a newline character?
Functions like ReadString('\n') include the newline in the returned string. Use strings.TrimSpace() to remove it.
Can fmt.Scanln read text with spaces?
It is not the best choice for free-form full-line input. For text with spaces, use bufio.Reader or bufio.Scanner.
Why does the program seem to exit immediately?
This often happens because the input function is used incorrectly, such as passing a non-pointer or using the wrong scan function.
Mini Project
Description
Build a small Go program that asks the user for their name and favorite programming language, then prints a friendly summary. This demonstrates how to read full-line console input safely and cleanly.
Goal
Create a console app that reads two lines of user input and displays them back without trailing newline characters.
Requirements
Requirement 1 Requirement 2 Requirement 3
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.