os.stdin 默认阻塞式读取,需回车或 ctrl+d/z 才返回;推荐用 bufio.scanner 按行读取并设 buffer 防超长行报错。

读 os.Stdin 时阻塞,但程序没报错也没退出
Go 的 os.Stdin 默认是阻塞式读取,没有换行或 EOF 就一直卡住。这不是 bug,是设计使然——它等你敲回车或 Ctrl+D(Linux/macOS)/Ctrl+Z(Windows)才返回。
- 用
bufio.Scanner最安全:自动按行切分,且默认有 64KB 行长限制,防内存爆掉 - 别直接用
io.ReadFull或io.Read读固定字节,容易卡死或截断输入 - 如果要读单个字符(比如确认 y/n),得先关闭输入缓冲:
os.Stdin.Sync()+bufio.NewReader(os.Stdin).ReadRune()
bufio.Scanner 报错 scanner: token too long
这是默认的 ScanLines 模式下,某一行超出了 64KB。不是数据有问题,是 scanner 的缓冲区太小。
- 临时解决:调用
scanner.Buffer(make([]byte, 64*1024), 1 把最大 token 设为 1MB - 长期建议:明确业务是否真需要超长单行——多数 CLI 工具应拒绝这种输入,而非硬撑
- 注意:增大 buffer 不影响性能,但会多占一点栈外内存;别设成
math.MaxInt32,Go 运行时可能 panic
从管道或重定向读 stdin 时,os.Stdin.Stat().Mode() & os.ModeCharDevice == 0 判断失效
这个判断本意是区分终端输入和文件/管道输入,但在某些容器环境、CI runner 或 Windows Git Bash 下会误判——os.ModeCharDevice 可能始终为 false,哪怕你正交互敲键盘。
- 更稳的方式是检查
os.Stdin.Fd()是否关联到终端:isTerminal := isatty.IsTerminal(int(os.Stdin.Fd()))(需引入golang.org/x/sys/unix或第三方github.com/mattn/go-isatty) - 如果只是想“有输入就读,没输入就跳过”,别依赖设备类型,改用非阻塞读法:
os.Stdin.Read()配合syscall.SetNonblock(仅限 Unix)或用time.AfterFunc包一层超时逻辑 - Windows 上
isatty比原生Stat().Mode()可靠得多
用 fmt.Scanf 读多个值时字段对不上
fmt.Scanf 对空白符敏感,且不跳过换行符。用户多敲一个空格、少敲一个回车,就可能导致后续读取错位甚至阻塞。
- 永远优先用
bufio.Scanner+strings.Fields或strconv.Parse*手动解析,控制权在自己手里 - 如果非用
fmt.Scanf,格式字符串必须严格匹配输入格式,比如"%s %d\n"要求输入后必须跟换行;"%s%d"则要求紧挨着输(如hello123) - 它不会帮你 trim 空格,也不会处理 Unicode 分隔符,中文输入法下的全角空格会直接导致匹配失败
stdin 读取最麻烦的从来不是语法,而是“用户到底敲了什么、什么时候敲完、敲的是不是你期待的格式”。别假设输入干净,也别迷信某个函数能兜底——每种读法都有它的边界,而边界之外,就是 EOF、invalid memory address 或无声的挂起。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











