
go 程序无法直接捕获 ctrl+d(eof),但可通过监听 `os.interrupt`(对应 ctrl+c)实现优雅退出,在终止前执行 ec2 清理等关键收尾逻辑。
在 Go 中,Ctrl + D 并非信号事件,而是终端输入流的 EOF(End-of-File)标志,通常影响 os.Stdin.Read() 等读取操作,不会向进程发送系统信号,因此无法“全局捕获”或用于触发中断处理逻辑。真正可用于优雅终止并执行清理操作的是 Ctrl + C —— 它会向进程发送 SIGINT 信号,Go 可通过 os/signal 包可靠监听并响应。
以下是标准、健壮的优雅退出模式:
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func cleanupEC2() error {
fmt.Println("⏳ Cleaning up EC2 resources...")
// 示例:调用 AWS SDK 终止实例、释放弹性 IP、删除安全组等
// e.g., ec2Client.TerminateInstances(...)
time.Sleep(1 * time.Second) // 模拟清理耗时
fmt.Println("✅ EC2 cleanup completed.")
return nil
}
func main() {
// 启动主业务逻辑(如轮询、HTTP 服务、长期任务)
fmt.Println("? Application started. Press Ctrl+C to exit gracefully.")
// 设置信号监听器
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // 同时支持 Ctrl+C 和 kill -15
// 启动一个 goroutine 处理信号
go func() {
sig := <-sigChan
fmt.Printf("\n⚠️ Received signal: %s. Initiating graceful shutdown...\n", sig)
// 执行清理逻辑(阻塞式,确保完成)
if err := cleanupEC2(); err != nil {
fmt.Printf("❌ Cleanup failed: %v\n", err)
}
fmt.Println("? Goodbye.")
os.Exit(0)
}()
// 模拟长期运行的任务(例如监控循环或 HTTP server)
select {} // 阻塞主 goroutine,等待信号
}✅ 关键要点说明:
- 使用 signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) 监听标准中断信号;
- SIGINT 对应 Ctrl+C(推荐方式),SIGTERM 支持 kill -15 等外部终止请求;
- 不要依赖 Ctrl+D 做退出控制——它仅关闭 stdin,对后台程序无意义,且不可靠(例如 go run 启动的程序通常不读 stdin);
- 清理逻辑应在信号处理 goroutine 中同步执行,避免竞态;若需超时保护,可结合 context.WithTimeout;
- os.Exit(0) 应在清理完成后显式调用,确保 defer 不再执行(因其已在主 goroutine 退出后失效)。
? 额外建议:
若程序本身需从 stdin 读取命令(如交互式 CLI),可同时监听 io.EOF(来自 Ctrl+D)作为用户主动退出输入流的提示,但此时仍应配合 Ctrl+C 信号机制保障资源安全释放——二者职责不同:前者是“输入结束”,后者才是“进程终止”。
通过该模式,你可在 EC2 创建中途被手动中断时,确保云资源不被遗忘,显著提升生产脚本的健壮性与运维安全性。










