CLI 本质小记
CLI(Command-Line Interface)不是什么高深概念,就是普通程序的另一种交互方式。
一句话
CLI 不是操作系统提供的特殊能力,每个 CLI 程序本质上就是一个普通的 .exe 可执行文件。
拆解
操作系统能运行的东西只有两种:
| GUI 程序 | CLI 程序 | |
|---|---|---|
| 入口 | WinMain() |
main() |
| 启动方式 | 双击图标 | 命令行输入名字 |
| 输入 | 鼠标点击、键盘事件 | os.Args(字符串数组) |
| 输出 | 画像素到屏幕 | 打印文字到 stdout |
shell 的角色
唯一特殊的是:操作系统提供了一个叫 shell(cmd.exe / PowerShell / bash)的程序,它帮你做三件事:
- 在
PATH环境变量里找到.exe - 把
todo done 3拆成["todo.exe", "done", "3"]传进main() - 把程序的 stdout 打印到终端窗口
你在终端敲: todo done 3
↓ ↓ ↓
shell 找到 → 拆成 → ["./todo.exe", "done", "3"]
todo.exe → main(os.Args) → 干活 → fmt.Println("✅")
结论
CLI 不是 OS 能力,每个 CLI 程序都只是一个按约定接收字符串参数、输出字符串到终端的普通程序。shell 是帮你省去"自己拼参数"这个麻烦的中间人。
番外:后台进程为什么挂掉
场景
用 todo-cli serve 启动了 Web 服务器,过一会就挂了。
原因:进程生命周期
用 Start-Process -NoNewWindow 启动的子进程,会绑定到当前 shell 会话的生命周期上:
你打开 PowerShell → 进程 PID=100 (shell 进程)
↓
你执行 Start-Process -NoNewWindow todo-cli.exe
↓
系统创建 PID=200(todo-cli,作为 PID=100 的子进程)
↓
你关闭 Terminal / shell 退出 / 网络断开
↓
系统回收 PID=100 时,连带杀掉所有子进程 PID=200
这就是"绑定在 shell 上"——子进程是 shell 进程的附属,shell 死它就死。
detach 模式为什么 OK
detach 模式做的事情:
Start-Process -NoNewWindow todo-cli.exe → 子进程,shell 死了它也死
使用 detach 启动时,系统会做 setsid() 调用 → 子进程脱离父进程独立
setsid() 是 Linux 的系统调用。Windows 上对应的是:
CREATE_NEW_PROCESS_GROUP标志位- 让新进程成为独立会话的首进程,不再受父 shell 生命周期约束
一句话
| 方式 | 关系 | 父 shell 退出后 |
|---|---|---|
直接启动(& / Start-Process) |
父子进程 | 子进程也被杀 |
| detach / setsid | 独立会话 | 进程继续运行 |
| 注册系统服务 / systemd | 守护进程 | 开机自启,永远活着 |
关联阅读:Windows Terminal + Copilot + DeepSeek 配置指南.md