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

