Read a character from standard input in Go (without pressing Enter)
Asked Answered
M

7

39

I want my app to show:

press any key to exit ...

And to exit when I press any key.

How can I achieve this?

Note: I have googled but all of what I've found needed to press Enter at the end. I want something like Console.ReadKey() in C#.

I am running MS Windows.

Minion answered 1/3, 2013 at 13:37 Comment(3)
possible duplicate of Golang function similar to getcharMassimo
@jnml No; this is not a duplicate. I've seen that question before and using the code provided in answer you still need to press enter at stdin.Minion
The duplicity is solely about the question, not about any answer to it.Massimo
C
14

The package "golang.org/x/term" allows you to switch the stdin into raw mode to read each byte at a time.

package main

import (
    "fmt"
    "os"

    "golang.org/x/term"
)

func main() {
    // switch stdin into 'raw' mode
    oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
    if err != nil {
        fmt.Println(err)
        return
    }
    defer term.Restore(int(os.Stdin.Fd()), oldState)

    b := make([]byte, 1)
    _, err = os.Stdin.Read(b)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("the char %q was hit", string(b[0]))
}
Crosspurpose answered 7/1, 2022 at 21:52 Comment(0)
P
37

This is a minimal working example for those running a UNIX system:

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    // disable input buffering
    exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()
    // do not display entered characters on the screen
    exec.Command("stty", "-F", "/dev/tty", "-echo").Run()

    var b []byte = make([]byte, 1)
    for {
        os.Stdin.Read(b)
        fmt.Println("I got the byte", b, "("+string(b)+")")
    }
}
Primalia answered 24/6, 2013 at 15:6 Comment(3)
How do you get the display the entered characters back on the screen?Recess
Ibolit: They already get displayed in the example. To display only the characters, fmt.Print(string(b)) should work.Primalia
lbolit: use exec.Command("stty", "-F", "/dev/tty", "echo").Run() to get them back.Cockney
M
15

termbox-go is a light-weight Go-native package which offers some rudimentary terminal control. Including the ability to get input in raw mode (read one character at a time without the default line-buffered behaviour).

It also has fairly ok compatibility across different systems.

And keyboard extends termbox-go to give some additional keyboard functionality like multi-key shortcuts and sequences.

Mortality answered 1/3, 2013 at 14:34 Comment(1)
That's not what the definition of raw_mode is.Priebe
C
14

The package "golang.org/x/term" allows you to switch the stdin into raw mode to read each byte at a time.

package main

import (
    "fmt"
    "os"

    "golang.org/x/term"
)

func main() {
    // switch stdin into 'raw' mode
    oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
    if err != nil {
        fmt.Println(err)
        return
    }
    defer term.Restore(int(os.Stdin.Fd()), oldState)

    b := make([]byte, 1)
    _, err = os.Stdin.Read(b)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("the char %q was hit", string(b[0]))
}
Crosspurpose answered 7/1, 2022 at 21:52 Comment(0)
C
10

go-termbox is very heavyweight. It wants to take over the entire terminal window. For example, it clears the screen on startup, which may not be what you want.

I put this together on OSX. Just a tiny getchar():

https://github.com/paulrademacher/climenu/blob/master/getchar.go

Cockaigne answered 14/8, 2015 at 21:10 Comment(1)
works on linux too, but it reads from the terminal, not stdin (which could be a pipe)Pesthole
H
10

You could use this library (mine): https://github.com/eiannone/keyboard

This is an example for getting a single keystroke:

char, _, err := keyboard.GetSingleKey()
if (err != nil) {
    panic(err)
}
fmt.Printf("You pressed: %q\r\n", char)
Hircine answered 5/5, 2016 at 13:39 Comment(1)
great library. At least for windows, this solution is much better and to the point than others.Normand
M
2

You can read a single key-press from a terminal in raw mode. Here is a package that should provide raw terminal mode to your program. Catch: it's Linux only.

Massimo answered 1/3, 2013 at 14:27 Comment(0)
G
0

Try this - http://play.golang.org/p/kg-QirlucY.

Just read from the os.Stdin at the end of the func main

Gizzard answered 25/6, 2013 at 4:32 Comment(1)
This code demonstrates how to use SetConsoleMode. If want "Press Enter to continue", then os.Stdin.Read([]byte{0}) is enough.Chassepot

© 2022 - 2024 — McMap. All rights reserved.