
本文介绍如何在 Go 程序中持续捕获并忽略 Ctrl+C(SIGINT)信号,避免其终止程序,同时保持标准输入读取逻辑正常运行——关键在于正确复用信号监听 goroutine,而非仅处理一次中断。
本文介绍如何在 go 程序中持续捕获并忽略 `ctrl+c`(sigint)信号,避免其终止程序,同时保持标准输入读取逻辑正常运行——关键在于正确复用信号监听 goroutine,而非仅处理一次中断。
在 Go 中,signal.Notify 本身并不会“消耗”或阻塞信号;它只是将接收到的信号转发到指定 channel。但若监听 goroutine 在首次接收信号后退出,后续 Ctrl+C 将不再被捕获——操作系统仍会向进程发送 SIGINT,而 Go 运行时默认行为是终止程序(除非显式屏蔽)。这就是你观察到“第一次有效、后续变成乱码或直接退出”的根本原因:初始 goroutine 已结束,信号无人接收,于是终端将 ^C 字符原样写入 stdin 缓冲区,被 ReadString('\n') 当作普通输入读取,导致 text 包含 \x03(即 ^C 的 ASCII 值),进而触发非预期的空输入逻辑。
要真正“忽略 Ctrl+C”,需做到两点:
- 持续监听:goroutine 必须长期运行,循环等待信号;
- 不中止主流程:信号到达后仅通知主线程,不 panic、不 os.Exit,更不关闭 stdin。
以下是修正后的完整实现:
package main
import (
"bufio"
"fmt"
"io"
"os"
"os/signal"
"strings"
"syscall"
)
func main() {
// 创建信号通道与中断通知通道
sigChan := make(chan os.Signal, 1)
intr := make(chan struct{}) // 使用空结构体更语义清晰(无需传值)
quit := make(chan struct{})
// 注册 SIGINT 监听
signal.Notify(sigChan, syscall.SIGINT)
// 启动持久化信号处理器 goroutine
go func() {
for {
select {
case <-sigChan:
fmt.Println("\nGot interrupt signal — ignored.")
intr <- struct{}{} // 通知主循环发生了中断
case <-quit:
fmt.Println("Signal handler shutting down.")
return
}
}
}()
// 主输入循环
for {
select {
case <-intr:
fmt.Println("Interrupted.. Continuing.")
continue // 立即重新提示输入,不重建 bufio.Reader
default:
fmt.Print("Enter text: ")
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err == io.EOF {
fmt.Println("Invalid input (EOF).")
continue
} else if err != nil {
fmt.Printf("Read error: %v\n", err)
continue
}
text = strings.TrimSpace(text)
if text == "" {
fmt.Println("Invalid input (empty).")
continue
}
fmt.Printf("Entered: %s\n", text)
goto done // 成功输入后退出
}
}
done:
close(quit) // 优雅关闭信号处理器
}✅ 关键改进说明:
- 使用 for-select 循环确保信号 goroutine 持续存活,可响应任意次数 Ctrl+C;
- intr channel 发送空结构体 struct{}{},零内存开销且语义明确(仅表示“事件发生”);
- goto done 替代 break breakOutOfHere,更简洁;
- close(quit) 在退出前通知监听 goroutine 结束,实现资源清理;
- 未调用 os.Stdin.Close() 或修改文件描述符——这是安全忽略信号的核心:不干扰 stdin 流状态。
⚠️ 注意事项:
- 此方案 忽略 信号,而非 屏蔽(syscall.Ignore)。若需彻底禁用 SIGINT(连内核都不发),可用 signal.Ignore(syscall.SIGINT),但通常不推荐——它会使 Ctrl+C 完全静默(无 ^C 回显),且无法在运行时恢复;
- 在某些终端(如 Windows CMD),^C 可能仍短暂显示,但不会中断程序——这是终端行为,不影响逻辑;
- 若程序需在后台运行或作为子进程被管理,请额外考虑 SIGHUP 和 SIGTERM 的处理策略。
通过以上设计,你的 Go 程序即可稳健地交互式运行,用户可随意按 Ctrl+C 而程序始终响应 Enter text: 提示,真正实现“输入不中断、体验不打断”。


















