CLI 本质小记

CLI(Command-Line Interface)不是什么高深概念,就是普通程序的另一种交互方式。

一句话

CLI 不是操作系统提供的特殊能力,每个 CLI 程序本质上就是一个普通的 .exe 可执行文件。

拆解

操作系统能运行的东西只有两种:

GUI 程序 CLI 程序
入口 WinMain() main()
启动方式 双击图标 命令行输入名字
输入 鼠标点击、键盘事件 os.Args(字符串数组)
输出 画像素到屏幕 打印文字到 stdout

shell 的角色

唯一特殊的是:操作系统提供了一个叫 shell(cmd.exe / PowerShell / bash)的程序,它帮你做三件事:

  1. PATH 环境变量里找到 .exe
  2. todo done 3 拆成 ["todo.exe", "done", "3"] 传进 main()
  3. 把程序的 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