406

I would like to read standard input from the command line, but my attempts have ended with the program exiting before I'm prompted for input. I'm looking for the equivalent of Console.ReadLine() in C#.

This is what I currently have:

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)
}
5
  • This code looks correct. Out of curiosity, are you running this on the Playground? The Go Playground doesn't allow stdin input due to networking reasons. Commented Jan 3, 2014 at 2:34
  • Nevermind, it looks to be a subtle issue where you needed a pointer (see my answer). Though I'm not sure what the problem with the bufio.NewReader method is since it works for me. Commented Jan 3, 2014 at 2:44
  • possible duplicate of Read from initial stdin in GO? Commented Feb 1, 2015 at 19:49
  • 12
    Don't mix bufio buffering of any reader (e.g. bufio.NewReader(os.Stdin)) with direct reads from the underlining reader (e.g. fmt.Scanln(x) directly reads from os.Stdin). Buffering may read arbitrarily far ahead. (In this specific case the later should be fmt.Fscanln(reader,x) to read from the same buffer). Commented Jul 2, 2015 at 14:05
  • I don't get fmt.Sscanln works, it becomes "%v" after running Commented Jan 8, 2018 at 4:39

13 Answers 13

431

I'm not sure what's wrong with the block

reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
fmt.Println(text)

As it works on my machine. However, for the next block you need a pointer to the variables you're assigning the input to. Try replacing fmt.Scanln(text2) with fmt.Scanln(&text2). Don't use Sscanln, because it parses a string already in memory instead of from stdin. If you want to do something like what you were trying to do, replace it with fmt.Scanf("%s", &ln)

If this still doesn't work, your culprit might be some weird system settings or a buggy IDE.

Sign up to request clarification or add additional context in comments.

8 Comments

Are those supposed to be single quotes? ReadString('\n') or ReadString("\n")?
@425nesp yes, that's the delimeter, which is a single byte. golang.org/pkg/bufio/#Reader.ReadString
Good answer, but this fails when I try using backspace, etc keys
So much for Golang to read a line from file via reader rd to variable s as if s,_ = rd.ReadString('\n'); true { s = strings.Trim(s, " \n") }
Just sharing an interesting thing (I'm a Golang beginner): \n must be inside single quotes (don't try to use double quotes). Or else, it'll reproduce this: cannot use "\n" (type string) as type byte in argument to reader.ReadString
|
176

You can as well try:

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}

if scanner.Err() != nil {
    // Handle error.
}

8 Comments

You can remove the "for {}" if you just want one a one line input.
if there a for {} loop, how to come out from loop as you enter? Is there a special character that will make for loop to stop? - Thanks
@Madhan scanner.Scan() returns bool value to indicate exiting the for loop or not.
You will get this error bufio.Scanner: token too long If your input is bigger than 64 * 1024 bytes. Also don't forget add fmt.Println(scanner.Err())below the for loop.
We cannot increase the limit. It is hardcoded into the go runtime.
|
135

I think a more standard way to do this would be:

package main

import "fmt"

func main() {
    fmt.Print("Enter text: ")
    var input string
    fmt.Scanln(&input)
    fmt.Print(input)
}

Take a look at the scan godoc: http://godoc.org/fmt#Scan

Scan scans text read from standard input, storing successive space-separated values into successive arguments. Newlines count as space.

Scanln is similar to Scan, but stops scanning at a newline and after the final item there must be a newline or EOF.

7 Comments

This doesn't seem to like spaces in the input string.
@HairyChris yes this is strange. In the doc it says that stops scanning at a newline and after the final item there must be a newline or EOF so not sure why space "breaks" it... I guess it is a bug
There was a bug opened for this: github.com/golang/go/issues/5703 It got closed as WorkingAsIntended. See also: stackoverflow.com/questions/24005899/… and groups.google.com/forum/#!topic/golang-nuts/r6Jl4D9Juw0 Seems a lot of people have issues with this. Documentation change needed? Also, from that last link: "Scan and Scanln are for parsing and stuff like that, so just getting a single line of text from stdin would defeat the purpose."
To me, its really confusing that fmt.Scan in any of its similar functions don't play well with spaces like the bufio.NewReader does.
The same problem with spaces remains while using fmt.Scanln and fmt.Scan with the current 2016 go version (go version go1.6.2 linux/amd64).
|
95

Always try to use the bufio.NewScanner for collecting input from the console. As others mentioned, there are multiple ways to do the job, but Scanner is originally intended to do the job. Dave Cheney explains why you should use Scanner instead of bufio.Reader's ReadLine.

https://web.archive.org/web/20190508023954/https://twitter.com/davecheney/status/604837853344989184

Here is the code snippet answer for your question

package main

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

/*
 Three ways of taking input
   1. fmt.Scanln(&input)
   2. reader.ReadString()
   3. scanner.Scan()

   Here we recommend using bufio.NewScanner
*/

func main() {
    // To create dynamic array
    arr := make([]string, 0)
    scanner := bufio.NewScanner(os.Stdin)
    for {
        fmt.Print("Enter Text: ")
        // Scans a line from Stdin(Console)
        scanner.Scan()
        // Holds the string that scanned
        text := scanner.Text()
        if len(text) != 0 {
            fmt.Println(text)
            arr = append(arr, text)
        } else {
            break
        }

    }
    // Use collected inputs
    fmt.Println(arr)
}

If you don't want to programmatically collect the inputs, just add these lines

   scanner := bufio.NewScanner(os.Stdin)
   scanner.Scan()
   text := scanner.Text()
   fmt.Println(text)

The output of above program will be:

Enter Text: Bob
Bob
Enter Text: Alice
Alice
Enter Text:
[Bob Alice]

The above program collects the user input and saves them to an array. We can also break that flow with a special character. Scanner provides API for advanced usage like splitting using a custom function, etc., scanning different types of I/O streams (standard Stdin, String), etc.

Edit: The tweet linked in original post is not accesible. But, one can find official reference of using Scanner from this standard library documentation: https://pkg.go.dev/[email protected]#example-Scanner-Lines

7 Comments

This should be the accepted answer. Not only is it a more accurate answer but it's of better quality.
This is so much better than the other input methods.
so, you shall use scanner.Scan() prior to scanner.Text() that is mandatory in order to parse the input for stdin ending with an enter \n, right?
@Victor, yes. If you read through the official bufio docs, "Text returns the most recent token generated by a call to Scan as a newly allocated string holding its bytes." which exactly answers your question. golang.org/pkg/bufio/#Scanner.Text
Thanks @NarenYellavula. I do wonder why they provide both functions by seperated instead of aggregate then (in the right order) into a another abstraction, e.g.: ReadLine() (see this example: play.golang.org/p/cR8XjJ6WHtq). Another question regarding my thoughts is... what is the motivation of using Scan() without Text() or Bytes()? You see, a wise thing to do is if two things goes together (Scan + Text) why don't put then together into a single abstraction.
|
16

Another way to read multiple inputs within a loop which can handle an input with spaces:

package main
import (
    "fmt"
    "bufio"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    var text string
    for text != "q" {  // break the loop if text == "q"
        fmt.Print("Enter your text: ")
        scanner.Scan()
        text = scanner.Text()
        if text != "q" {
            fmt.Println("Your text was: ", text)
        }
    }
}

Output:

Enter your text: Hello world!
Your text was:  Hello world!
Enter your text: Go is awesome!
Your text was:  Go is awesome!
Enter your text: q

2 Comments

You could maybe just use a break in the inner "q" check and wrap it all in an infinite loop. Great answer by the way!
Looks like you can also get rid of the conditional in the for loop now too.
13

I'm late to the party. But how about one liner:

data, err := io.ReadAll(os.Stdin)

And press ctrl+d once done.

3 Comments

Because os.Stdin doesn't 'end' it's impossible to read it all. You might be waiting a while...
press ctrl+d i.e. eot.
Yes, that'd do it - reminds me of writing emails with mail.
12

It can also be done like this:

package main
import "fmt"

func main(){
    var myname string
    fmt.Scanf("%s", &myname)
    fmt.Println("Hello", myname)
}

1 Comment

Besides repeating what others have already written a thousand times, fmt.Scanf() stops with the first space.
5

Cleanly read in a couple of prompted values:

// Create a single reader which can be called multiple times
reader := bufio.NewReader(os.Stdin)
// Prompt and read
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
fmt.Print("Enter More text: ")
text2, _ := reader.ReadString('\n')
// Trim whitespace and print
fmt.Printf("Text1: \"%s\", Text2: \"%s\"\n",
    strings.TrimSpace(text), strings.TrimSpace(text2))

Here's a run:

Enter text: Jim
Enter More text: Susie
Text1: "Jim", Text2: "Susie"

2 Comments

Also a nice way since strings.TrimSpace removes the '\n'. And I believe reader.ReadString('\n') is cross platform too.
I'll guess that most of the time you want to remove \n by default, that it's why it is better bufio.NewScanner as @Naren Yellavula answer
5

Try this code:

var input string
func main() {
    fmt.Print("Enter Your Name=")
    fmt.Scanf("%s", &input)
    fmt.Println("Hello " + input)
}

1 Comment

Seems like Scanf() doesn't accept white spaces in string
5

You need to provide a pointer to the variable you want to scan, like so:

fmt.scan(&text2)

Comments

3

In my case, the program was not waiting because I was using the watcher command to auto run the program. Manually running the program go run main.go resulted in "Enter text" and eventually printing to the console.

fmt.Print("Enter text: ")
var input string
fmt.Scanln(&input)
fmt.Print(input)

2 Comments

The limitation of the Scan* family is that they read up to a whitespace (e.g. space) separator.
See the other answers above...
0

Let's do it very simple

s:=""
b := make([]byte, 1)
for os.Stdin.Read(b) ; b[0]!='\n'; os.Stdin.Read(b) {
    s+=string(b)
}
fmt.Println(s)

3 Comments

Isn't that super slow?!
You should be using strings.Builder and strings.WriteString to construct such a string. Example from standard library.
Simple != Correct
0

Here's the sane way of reading more than 1024 characters from the terminal. As-is it won't work with pipes and error, you can also opt to read using other methods (such as reading with bufio.Scanner or io.ReadAll) in that case.

package main

import (
    "fmt"
    "os"

    "golang.org/x/term"
)

func readLine() (string, error) {
    if !term.IsTerminal(int(os.Stdin.Fd())) {
        return "", fmt.Errorf("pipe not supported")
    }

    oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
    if err != nil {
        return "", fmt.Errorf("failed setting stdin to raw mode: %w", err)
    }
    tty := term.NewTerminal(os.Stdin, "")
    line, err := tty.ReadLine()
    _ = term.Restore(int(os.Stdin.Fd()), oldState)

    if err != nil {
        return "", fmt.Errorf("failed to read from stdin: %w", err)
    }
    return line, nil
}

func main() {
    fmt.Println("Enter your line:")
    line, err := readLine()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Your line was %d characters: %q\n", len(line), line)
}

1 Comment

Talk about overkill!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.