用 bufio.Scanner 读取用户输入最稳妥,因其以换行符为分隔、Scan() 返回布尔值指示成功、Text() 获取无换行符内容,且可优雅处理 EOF;而 fmt.Scanln 易因空格截断,Scanf 和 ReadString('\n') 存在卡住或缓冲区残留风险。

用 bufio.Scanner 读取用户输入最稳妥
Go 标准库没有 readline 类似物,fmt.Scanln 在遇到空格或换行时行为容易出错,尤其读取含空格的字符串(比如文件路径、用户名)会截断。直接用 os.Stdin 配合 bufio.Scanner 是最可控的方式。
-
Scanner默认以换行符为分隔,Scan()返回true表示成功读到一行,Text()获取内容(不含换行符) - 避免用
Scanf或ReadString('\n'):前者格式化失败会卡住,后者可能因缓冲区残留导致跳过输入 - 需要支持 Ctrl+D(EOF)退出?检查
err == io.EOF即可,不用额外处理
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("→ ")
if !scanner.Scan() {
if errors.Is(scanner.Err(), io.EOF) {
break
}
log.Fatal(scanner.Err())
}
input := scanner.Text()
// 处理 input...
}
解析命令参数用 flag 还是 github.com/spf13/cobra?
如果只是简单开关(如 -v、--output=file.txt),flag 包足够且无依赖;但一旦要支持子命令(app init、app run --port=8080)、自动 help 输出、bash 补全,cobra 是事实标准。
-
flag的坑:不支持子命令,flag.Parse()会吞掉所有参数直到第一个非 flag 字符,后续参数得手动从flag.Args()拿 -
cobra初始化后,cmd.Execute()自动处理--help和未知 flag,错误提示也更友好 - 注意
cobra的Args字段(如cobra.ExactArgs(1))比手写校验更可靠,避免运行时 panic
交互式确认别用 fmt.Print + Scanln 拼逻辑
用户输入 y、yes、Y、空格加 YES 都该算“确认”,手写字符串比较容易漏 case。封装一个健壮的 confirm 函数更省心。
- 统一转小写 +
strings.TrimSpace再判断,覆盖常见变体 - 提供默认值提示(如
[y/N]),并在用户直接回车时返回默认值 - 不要循环重试太多次——3 次输错就
os.Exit(1),避免卡死
func confirm(prompt string, def bool) bool {
for i := 0; i < 3; i++ {
fmt.Printf("%s [%s/%s]: ", prompt,
map[bool]string{true: "Y", false: "N"}[def],
map[bool]string{true: "n", false: "y"}[def])
var input string
if _, err := fmt.Scanln(&input); err != nil {
continue
}
input = strings.ToLower(strings.TrimSpace(input))
if input == "" {
return def
}
if input == "y" || input == "yes" || input == "t" || input == "true" {
return true
}
if input == "n" || input == "no" || input == "f" || input == "false" {
return false
}
}
os.Exit(1)
return def
}
清屏和光标控制尽量避开 ANSI 转义序列硬编码
虽然 fmt.Print("\033[2J\033[H") 能清屏,但它在 Windows CMD(非 PowerShell)、某些 IDE 内置终端里不生效,还可能被误当乱码输出。真需要清屏,优先调用系统命令。
立即学习“go语言免费学习笔记(深入)”;
- Linux/macOS 用
exec.Command("clear").Run(),Windows 用exec.Command("cmd", "/c", "cls").Run() - 光标定位(如进度条覆盖上一行)用
\r比\033[A兼容性好得多,尤其对 CI 日志输出友好 - 如果必须用 ANSI(比如彩色日志),检查
os.Getenv("TERM")是否为空或等于"dumb",是则跳过所有转义序列
signal.Notify 捕获 os.Interrupt 后,记得关闭 Scanner 或释放资源,否则可能残留 goroutine。


















