You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go跨平台编译时bufio.NewScanner在Windows下输入被跳过问题

问题描述

我有一段参考公开实现编写的Go AWS SSO登录工具代码,具体如下:

fmt.Println("Please enter your role: ")
fmt.Scanf("%s", &roleName)

flag.StringVar(&startURL, "start-url", "", "AWS SSO Start URL")
flag.StringVar(&accountID, "account-id", "", "AWS Account ID to fetch credentials for")
flag.Parse()
if startURL == "" || accountID == "" || roleName == "" {
    flag.Usage()
    os.Exit(1)
}

cfg := aws.Config{Region: "eu-west-1"}

// 创建SSO OIDC客户端触发登录流程
ssooidcClient := ssooidc.NewFromConfig(cfg)

// 注册发起登录流程的客户端
register, err := ssooidcClient.RegisterClient(context.TODO(), &ssooidc.RegisterClientInput{
    ClientName: aws.String("sample-client-name"),
    ClientType: aws.String("public"),
    Scopes:     []string{"sso-portal:*"},
})
if err != nil {
    fmt.Println(err)
}
// 使用客户端注册返回信息完成设备授权
deviceAuth, err := ssooidcClient.StartDeviceAuthorization(context.TODO(), &ssooidc.StartDeviceAuthorizationInput{
    ClientId:     register.ClientId,
    ClientSecret: register.ClientSecret,
    StartUrl:     aws.String(startURL),
})
if err != nil {
    fmt.Println(err)
}
// 触发OIDC登录,拉起浏览器,登录完成关闭标签页后按回车继续
url := aws.ToString(deviceAuth.VerificationUriComplete)
fmt.Printf("If browser is not opened automatically, please open link:\n%v\n", url)
err = browser.OpenURL(url)
if err != nil {
    fmt.Println(err)
}

fmt.Println("Press ENTER key once login is done")
// 以下代码在Windows环境会被直接跳过
// 已尝试替换为bufio.NewReader(os.Stdin).ReadBytes('\n')
// 以及fmt.Scanf("%s", &test)均无法解决问题
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if scanner.Err() != nil {
    fmt.Println()
}

token, err := ssooidcClient.CreateToken(context.TODO(), &ssooidc.CreateTokenInput{
    ClientId:     register.ClientId,
    ClientSecret: register.ClientSecret,
    DeviceCode:   deviceAuth.DeviceCode,
    GrantType:    aws.String("urn:ietf:params:oauth:grant-type:device_code"),
})

程序在macOS环境运行正常,会按逻辑等待用户输入,AWS SSO登录流程可以完整执行;但在Windows环境下,提示用户登录完成后按回车的等待输入逻辑会被直接跳过,用户还没完成AWS侧的登录授权,程序就会直接报错。
需要注意的是,代码开头请求用户输入角色名的交互逻辑可以正常运行,只有后续等待回车确认的逻辑异常。我在macOS设备上通过以下命令执行跨平台编译,生成对应系统的二进制文件:

GOOS=darwin go build
GOOS=windows go build
问题原因

根本原因是开头的fmt.Scanf("%s", &roleName)在不同系统下的行为存在差异:

  • macOS这类类Unix系统中,Scanf按%s格式读取输入时,碰到换行符就会停止读取,同时会把输入缓冲区里的换行符一并消费掉
  • Windows终端的行处理规则和类Unix系统不同,Scanf("%s")读完用户输入的角色名字符串后,用户敲回车产生的\r\n换行符会残留在标准输入缓冲区中,不会被读取消费。
    后续不管是用Scanner.Scan()还是ReadBytes('\n')等待输入,一启动就会直接读到缓冲区里残留的这个换行符,立刻返回,表现出来就是等待输入的逻辑被直接跳过。开头读角色名时逻辑正常,是因为那是程序启动后第一次读取标准输入,缓冲区初始为空,没有残留内容。
    另外这个问题和跨平台编译操作无关,是不同系统终端IO行为差异+混用不同输入API导致的缓冲区残留问题。
修复方法

选择任意一种方案即可解决:

  • 快速修复:在读取角色名的fmt.Scanf("%s", &roleName)代码后,主动消费掉标准输入缓冲区里残留的换行符:
    // 读取残留的回车换行,避免后续输入逻辑被跳过
    _, _ = fmt.Scanln()
    
    如果担心有多余的残留字符,可以直接用bufio把当前行剩余内容全部读走丢弃:
    stdin := bufio.NewReader(os.Stdin)
    // 放在读取roleName的逻辑之后执行
    _, _ = stdin.ReadString('\n')
    
    后续等待用户按回车的逻辑直接复用这个stdin实例即可,不要重复创建新的reader。
  • 根治方案:全程统一使用bufio读取所有用户输入,不要混用fmt包的Scan系列方法和bufio的读取方法,从根源上避免不同输入API对缓冲区处理逻辑不一致导致的残留问题。把开头读取角色名的逻辑也改成bufio实现,就不会出现这类跨平台输入异常。

内容的提问来源于stack exchange,提问作者Baptise

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 01:48:46