Go言語で標準入力のキー押下(KeyPress
)を検知する
Golang で標準入力から1行ごとではなく、1文字だけ取得する方法を知りたい。
正確には TTY からの標準入力を 1 文字欲しいのです。Javascript で言うところの onkeypress
のキーイベントを取得したいのです。
つまり、ターミナルやコマンドラインなどのユーザー入力(標準入力)で y
-> enter
でなく、y
キーを押しただけで y
を検知したいのです。
「golang 標準入力 1文字 読み込む」で Qiita 記事に絞ってググっても、競プロの課題にあるような「改行区切りで得られた文字列から、最初の文字を取得する」ような例やパイプ渡しの例ばかりだったので、自分のググラビリティとして。
TL; DR (今北産業)
-
こういうこと 👇 をしたい人向け
$ go run . <enter> Key press => a Key press => b Key press => c Key press => d ...(ctrl+c) signal: interrupt
-
@mattn さんの素晴らしい
github.com/mattn/go-tty
パッケージのtty.ReadRune()
を使う。- ソース: https://github.com/mattn/go-tty @ GitHub
- ドキュメント: https://pkg.go.dev/github.com/mattn/go-tty#section-readme
(こいつやー!と思ったらスターを付けましょう)
-
tty
入力を監視し、1 文字(rune
)検知するごとにゴニョゴニョする。
- 動作確認済み環境
- Intel Core i5(2.7 GHz デュアルコア)
- macOS Catalina 10.15.7, Debian GNU/Linux 10 (buster), Alpine Linux v3.13
- ARMv7 Processor rev 4 (v7l)
- Raspbian GNU/Linux 9 (stretch),
- Go version 1.16
- Intel Core i5(2.7 GHz デュアルコア)
- 併せて読みたい
TS; DR (マスター、動くものをくれ)
package main
import (
"fmt"
"log"
"github.com/mattn/go-tty"
)
func main() {
tty, err := tty.Open()
if err != nil {
log.Fatal(err)
}
defer tty.Close()
fmt.Println("Ready. Press any key ...")
for {
r, err := tty.ReadRune()
if err != nil {
log.Fatal(err)
}
fmt.Println("Key press => " + string(r))
}
}
Go Playground では tty
からの標準入力のテストができないため、再現性のために Dockerfile も置いておきます。
module sample/tty
go 1.16
require github.com/mattn/go-tty v0.0.3
FROM golang:1.16-alpine
COPY . /app
WORKDIR /app
RUN go mod tidy
$ # ファイルの確認
$ tree .
.
├── Dockerfile
├── go.mod
└── main.go
0 directories, 2 files
$ # 実行環境(コンテナ・イメージ)のビルド
$ docker build --tag sample/tty .
...
$ # `go run .` と同じことをコンテナ内で実行
$ docker run --rm --interactive --tty sample/tty go run .
Ready. Press any key ...
Key press => a
Key press => b
Key press => c
Key press => d
Key press => e
(ctrl+c)
signal: interrupt
$
macOS とラズパイ3+B(Raspbian GNU/Linux 9, stretch)の Docker で動きました。また、Docker 内で darwin
と arm
向けに静的ビルドすれば、ビルドしたバイナリをエクスポートしてローカルで実行しても動きました。