如何在不进入Golang循环的情况下捕获按键

时间:2019-01-29 13:34:53

标签: go concurrency stdin keypress nonblocking

我有一个循环,其中根据运行状态(手动/自动/学习)发生了一些事情。现在,我希望能够通过按键盘上的字母来使程序在这些状态之间切换(“ m”表示手动,“ a”表示自动,“ l”表示学习)。

因此,要做到这一点,我需要能够在循环中捕捉按键并相应地更改变量状态。我现在有以下内容,可以在按下Enter键后捕捉按键:

ch := make(chan string)
go func(ch chan string) {
    reader := bufio.NewReader(os.Stdin)
    for {
        s, _ := reader.ReadString('\n')
        ch <- s
    }
}(ch)

for {
    select {
        case stdin, _ := <-ch:
            fmt.Println("Keys pressed:", stdin)
        default:
            fmt.Println("Working..")
    }
    time.Sleep(time.Second)
}

但是我不能按下Enter键这一事实是不可接受的。

有人知道一种非阻塞的方式来捕获普通字母(不是SIGINT)的按键,而无需随后按Enter键吗?

2 个答案:

答案 0 :(得分:1)

因为您使用的是ReadString键,所以它会根据您的情况提供给您的参数。 根据{{​​3}}:

  

ReadString读取直到输入中第一次出现delim为止,   返回一个字符串,该字符串包含直至(包括)   定界符。

这意味着该方法只有在您按下return键后才会返回。

您可以改用常规的the docs方法来读取所需的字符。 另请参阅此Read以供参考。

答案 1 :(得分:0)

好,在阅读了os.Stdin.Read()并找到this answer之后,我创建了以下代码:

package main

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

func main() {
    ch := make(chan string)
    go func(ch chan string) {
        // 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)
            ch <- string(b)
        }
    }(ch)

    for {
        select {
            case stdin, _ := <-ch:
                fmt.Println("Keys pressed:", stdin)
            default:
                fmt.Println("Working..")
        }
        time.Sleep(time.Millisecond * 100)
    }
}

这就像一种魅力,所以我只为将来的读者发布。

无论如何,祝你美好的一天!