Git Bash中用户输入无法终止问题排查(Go语言Git钩子场景)
Git钩子Go语言输入在Git Bash中卡住的问题排查与解决
问题背景
在commit-msg钩子中用Go语言收集用户输入,Windows下打开CON设备,Mac/Linux下打开/dev/tty获取输入,该方案在PowerShell和CMD中运行正常,但在Git Bash环境下,输入内容并按回车后,程序始终处于等待输入的状态,无法继续执行。
附原代码(存在语法错误):
con, err := os.Open("CON") if err == nil { os.Stdin = con reader := bufio.NewReader(os.Stdin) line, _ := reader.ReadString('\n' // 缺少闭合括号 }
原因分析
- Git Bash终端模拟特性:Git Bash基于MinGW/MSYS2模拟类Unix终端,Windows原生的
CON设备在该环境下的输入流处理逻辑与CMD/PowerShell不同。直接将os.Stdin替换为CON的文件句柄后,bufio.ReadString('\n')无法正确识别Git Bash终端输入的换行信号,导致程序一直阻塞等待。 - 代码语法错误:原代码中
ReadString('\n'缺少闭合括号,会引发编译异常,虽不直接导致Git Bash的阻塞问题,但属于必须修复的基础错误。 - 跨平台终端兼容性缺陷:直接操作系统原生设备文件(
CON//dev/tty)缺乏统一的终端输入处理逻辑,不同终端环境下的换行符、输入缓冲机制差异会导致行为不一致。
解决方法
方法1:使用专业终端处理包(推荐)
借助golang.org/x/term包,它提供跨平台的终端输入处理能力,可自动适配Git Bash等不同终端环境,无需手动处理设备文件。
示例代码:
package main import ( "bufio" "fmt" "os" "golang.org/x/term" ) func main() { // 保存原始终端状态,程序结束后恢复 oldState, err := term.MakeRaw(int(os.Stdin.Fd())) if err != nil { fmt.Printf("设置终端原始模式失败:%v\n", err) return } defer term.Restore(int(os.Stdin.Fd()), oldState) reader := bufio.NewReader(os.Stdin) fmt.Print("请输入内容:") line, err := reader.ReadString('\n') if err != nil { fmt.Printf("读取输入失败:%v\n", err) return } fmt.Printf("你输入的内容是:%s", line) }
方法2:避免替换os.Stdin,直接使用打开的设备文件
不覆盖全局的os.Stdin,直接用打开的CON或/dev/tty文件作为输入源,同时处理不同终端的换行符差异:
示例代码:
package main import ( "bufio" "fmt" "os" "strings" ) func main() { var inputFile *os.File var err error // 根据系统选择设备文件 if isWindows() { inputFile, err = os.Open("CON") } else { inputFile, err = os.Open("/dev/tty") } if err != nil { fmt.Printf("打开终端失败:%v\n", err) return } defer inputFile.Close() reader := bufio.NewReader(inputFile) fmt.Print("请输入内容:") line, err := reader.ReadString('\n') if err != nil { fmt.Printf("读取输入失败:%v\n", err) return } // 处理Git Bash中的换行符(移除可能的\r) line = strings.TrimSuffix(line, "\r\n") line = strings.TrimSuffix(line, "\n") fmt.Printf("你输入的内容是:%s\n", line) } func isWindows() bool { return os.PathSeparator == '\\' && os.PathListSeparator == ';' }
方法3:判断Git Bash环境并适配
通过环境变量MSYSTEM判断当前是否处于Git Bash环境,若是则直接使用os.Stdin而非CON设备:
示例代码片段:
func main() { var inputFile *os.File var err error if isWindows() { // 检查是否是Git Bash环境 if os.Getenv("MSYSTEM") != "" { inputFile = os.Stdin } else { inputFile, err = os.Open("CON") } } else { inputFile, err = os.Open("/dev/tty") } // 后续读取逻辑同方法2 }
内容的提问来源于stack exchange,提问作者Kevin Zhou
相关产品推荐
相关产品推荐

