Go 入门(25):练习——CLI 待办事项工具
更新时间:2026-09-01。本文是
languages/go/beginner/入门层第 25 篇,接 猜数字游戏。综合练习写一个 CLI 待办事项工具,支持添加、列出、完成、删除任务,数据持久化到文件。
需求
todo add "任务描述"→ 添加一项任务todo list→ 列出所有任务,显示完成状态todo done 1→ 标记第 1 项任务为完成todo delete 1→ 删除第 1 项任务- 数据保存到
~/.todo.json,程序退出后数据不丢失
用到的知识
flag包解析子命令和参数os包读写文件encoding/json序列化/反序列化- 结构体组织任务数据
os.UserHomeDir()获取用户目录
实现
todo.go:
go
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
)
type Task struct {
ID int `json:"id"`
Desc string `json:"desc"`
Complete bool `json:"complete"`
}
type TodoList struct {
Tasks []Task `json:"tasks"`
NextID int `json:"next_id"`
}
func (t *TodoList) save(path string) error {
data, err := json.MarshalIndent(t, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, data, 0644)
}
func loadTodo(path string) (*TodoList, error) {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return &TodoList{}, nil
}
return nil, err
}
var list TodoList
err = json.Unmarshal(data, &list)
return &list, err
}
func main() {
if len(os.Args) < 2 {
fmt.Println("用法: todo <add|list|done|delete> [args]")
os.Exit(1)
}
home, _ := os.UserHomeDir()
path := filepath.Join(home, ".todo.json")
list, err := loadTodo(path)
if err != nil {
fmt.Fprintf(os.Stderr, "load error: %v\n", err)
os.Exit(1)
}
command := os.Args[1]
switch command {
case "add":
if len(os.Args) < 3 {
fmt.Println("用法: todo add <描述>")
os.Exit(1)
}
desc := os.Args[2]
list.Tasks = append(list.Tasks, Task{
ID: list.NextID,
Desc: desc,
})
list.NextID++
fmt.Printf("添加任务 | %s\n", desc)
case "list":
if len(list.Tasks) == 0 {
fmt.Println("没有待办事项")
return
}
for _, t := range list.Tasks {
status := " "
if t.Complete {
status = "✓"
}
fmt.Printf("[%s] %d: %s\n", status, t.ID, t.Desc)
}
case "done":
if len(os.Args) < 3 {
fmt.Println("用法: todo done <id>")
os.Exit(1)
}
id, _ := strconv.Atoi(os.Args[2])
for i := range list.Tasks {
if list.Tasks[i].ID == id {
list.Tasks[i].Complete = true
fmt.Printf("完成 | %s\n", list.Tasks[i].Desc)
break
}
}
case "delete":
if len(os.Args) < 3 {
fmt.Println("用法: todo delete <id>")
os.Exit(1)
}
id, _ := strconv.Atoi(os.Args[2])
for i, t := range list.Tasks {
if t.ID == id {
list.Tasks = append(list.Tasks[:i], list.Tasks[i+1:]...)
fmt.Printf("删除 | %s\n", t.Desc)
break
}
}
default:
fmt.Printf("未知命令: %s\n", command)
os.Exit(1)
}
if err := list.save(path); err != nil {
fmt.Fprintf(os.Stderr, "save error: %v\n", err)
os.Exit(1)
}
}编译和执行
bash
go build -o todo todo.go
./todo add "学习 Go 切片"
./todo add "写一篇笔记"
./todo list
# [ ] 1: 学习 Go 切片
# [ ] 2: 写一篇笔记
./todo done 1
./todo list
# [✓] 1: 学习 Go 切片
# [ ] 2: 写一篇笔记扩展练习
- 加
--sort参数:按完成状态排序,未完成的排前面 - 加优先级:
todo add "任务" --priority high - 加截止日期:
todo add "任务" --due 2026-09-10 - 支持
todo find "关键词"搜索任务
相关与延伸
下一篇:练习:HTTP 接口服务;flag 包用法见 命令行参数;JSON 序列化见 JSON 序列化;文件 I/O 见 文件 I/O。
一句话总结
CLI 待办事项工具:把前 15 篇的知识点组合起来——结构体组织数据、JSON 持久化到文件、flag 和 os.Args 解析命令、switch 分发命令。一个完整的"写了就能用"的实用工具。