Go程序输出时如何实现控制台输入控制程序终止?
解决Go应用中输入输出冲突的问题
你的问题本质是终端规范模式导致输入与输出共用同一光标位置,大量输出会直接覆盖正在输入的内容。下面提供两种可行的解决方法:
方法一:标准库实现终端原始模式
通过修改终端属性,让输入在后台读取,手动控制输入回显和光标位置,避免输出干扰输入行:
package main import ( "bufio" "fmt" "os" "syscall" "unsafe" ) type Termios struct { Iflag uint32 Oflag uint32 Cflag uint32 Lflag uint32 Cc [32]uint8 Ispeed uint32 Ospeed uint32 } var ( process bool = true originalTerm Termios ) func saveTerminalState(fd uintptr) error { _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, syscall.TCGETS, uintptr(unsafe.Pointer(&originalTerm))) if errno != 0 { return errno } return nil } func restoreTerminalState(fd uintptr) { syscall.Syscall(syscall.SYS_IOCTL, fd, syscall.TCSETS, uintptr(unsafe.Pointer(&originalTerm))) } func commandHandler() { defer restoreTerminalState(os.Stdin.Fd()) // 设置终端为原始模式,关闭规范模式与自动回显 var rawTerm Termios rawTerm = originalTerm rawTerm.Lflag &^= syscall.ICANON | syscall.ECHO rawTerm.Cc[syscall.VMIN] = 1 rawTerm.Cc[syscall.VTIME] = 0 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, os.Stdin.Fd(), syscall.TCSETS, uintptr(unsafe.Pointer(&rawTerm))) if errno != 0 { fmt.Println("Failed to set terminal to raw mode:", errno) return } reader := bufio.NewReader(os.Stdin) inputBuf := make([]byte, 0, 100) for process { b, err := reader.ReadByte() if err != nil { break } switch b { case '\n', '\r': cmd := string(inputBuf) if cmd == "stop" { process = false fmt.Println("\nStopping...") } inputBuf = inputBuf[:0] fmt.Print("\n> ") case '\b': if len(inputBuf) > 0 { inputBuf = inputBuf[:len(inputBuf)-1] fmt.Print("\b \b") } default: inputBuf = append(inputBuf, b) fmt.Print(string(b)) } } } func task() { var i int64 = 0 for process && i < 10000000000 { if i%30000000 == 0 { fmt.Printf("\r%d\n> ", i) } i++ } } func main() { err := saveTerminalState(os.Stdin.Fd()) if err != nil { fmt.Println("Failed to save terminal state:", err) return } fmt.Println("Enter 'stop' to terminate:") fmt.Print("> ") go task() commandHandler() }
方法二:使用第三方终端库(更简洁)
借助成熟的第三方库github.com/pkg/term封装终端控制细节,减少手动处理的代码:
package main import ( "fmt" "github.com/pkg/term" "os" ) var process bool = true func commandHandler() { t, err := term.Open("/dev/tty") if err != nil { fmt.Println("Failed to open terminal:", err) return } defer t.Close() defer term.Restore(os.Stdin.Fd()) term.RawMode(t) fmt.Println("Enter 'stop' to terminate:") fmt.Print("> ") inputBuf := make([]byte, 0, 100) for process { b := make([]byte, 1) n, err := t.Read(b) if err != nil || n == 0 { break } switch b[0] { case '\n', '\r': cmd := string(inputBuf) if cmd == "stop" { process = false fmt.Println("\nStopping...") } inputBuf = inputBuf[:0] fmt.Print("\n> ") case '\b': if len(inputBuf) > 0 { inputBuf = inputBuf[:len(inputBuf)-1] fmt.Print("\b \b") } default: inputBuf = append(inputBuf, b[0]) fmt.Print(string(b)) } } } func task() { var i int64 = 0 for process && i < 10000000000 { if i%30000000 == 0 { fmt.Printf("\r%d\n> ", i) } i++ } } func main() { go task() commandHandler() }
核心逻辑说明
- 通过切换终端到原始模式,分离输入与输出的光标控制,避免输出覆盖输入行
- 手动处理输入回显、退格等操作,保证正常的输入交互体验
- 输出时用
\r和\n组合,将光标定位到新行,不干扰输入提示符
内容的提问来源于stack exchange,提问作者Nazarevsky
相关产品推荐
相关产品推荐

