1. 先搞清楚a2d-diary到底解决什么问题
市面上的笔记软件和日记工具我基本都折腾过一遍,要么太重(一套 Electron 应用吃我几百兆内存),要么太封闭(数据锁在私有格式里,想导出比搬家还难)。而 Python 生态里的 a2d-diary 属于另一类东西:一个跑在终端里的纯文本日记工具包。它不提供花哨的图形界面,而是把“记日记”这件事拆成了一组可编程的语法命令和参数接口,让日记变成一份带时间索引、可检索、可渲染成 Markdown 的本地文件集合。
a2d-diary 解决的核心痛点有三个。第一,随手记:打开终端敲一行命令就能写,不需要等编辑器慢慢启动。第二,数据结构化:每条日记自动带上日期、时间、标签等元信息,而不是像纯文本文件那样写完就变成一个无从检索的孤岛。第三,可编程:因为本质是个 Python 包,你可以把它嵌进自动化脚本里,比如下班时自动把今天的 git 提交记录汇总成日报,或者定时器触发某个复盘模板。这三个特性让它在程序员、写作者、做知识管理的人手里特别顺手。
这个包适合谁来用?我个人的判断是:如果你已经有使用终端的习惯,或者日常工作流里离不开 Python 脚本,那 a2d-diary 的学习成本几乎为零,它给你的回报是长期、稳定、可迁移的日记数据。如果你完全不碰命令行,那还是继续用图形界面工具更舒服,不要为难自己。
下面我先把整个包的设计思路拆开讲,再逐个过语法和参数,最后给几个我可以直接抄作业的实战场景,以及我在真实使用中踩过的一些坑。
2. 整体设计思路与安装准备
2.1 为什么选择“命令 + 文件目录”的架构
a2d-diary 的设计思路很像 Unix 哲学:一件事只做一件事,但把这件事做到极致。它不尝试在一个工具里塞进日历、待办、思维导图,它只负责“按时间记录文本”,剩下的工作全部留给文件系统和你的其他工具链。
安装之后,它会默认在你的用户目录下建立一个日记文件夹(通常是~/diary或~/.a2d,取决于版本配置),里面按年份和月份划分子目录,例如:
diary/ ├── 2024/ │ ├── 01/ │ │ ├── 2024-01-01.md │ │ ├── 2024-01-02.md │ │ └── ... │ ├── 02/ │ │ ├── 2024-02-01.md │ │ └── ... │ └── ... └── 2025/ ├── 03/ │ ├── 2025-03-15.md │ └── ...这个结构的优点你一旦开始用就会体会到:哪怕哪一天 a2d-diary 这个包彻底不维护了、卸了,你的日记依然是一堆标准的 Markdown 文件。任何编辑器都能打开,任何脚本都能处理,数据永远在你手里,而不是锁在某个服务商的数据库里。
每条日记文件内部也有固定的结构。默认会生成一个带 YAML 头部的 Markdown 文件,头部存元信息(日期、标签、天气、睡眠时长等自定义字段),正文就是自由文本。这样做的好处是,检索和统计可以直接解析头部字段,而不用全文匹配关键词。这种设计在知识管理领域叫“内容与元数据分离”,听起来高大上,其实就是让程序容易读懂你的日记。
2.2 安装方式与版本环境说明
安装 a2d-diary 的方式非常常规,走 PyPI 通道:
pip install a2d-diary如果你用的是国内网络环境,建议指定清华镜像源,速度会明显快一些:
pip install a2d-diary -i https://pypi.tuna.tsinghua.edu.cn/simple如果你担心污染系统级 Python 环境,我更推荐装到虚拟环境里。我的习惯是创建一个专门的env目录:
python -m venv ~/.venvs/a2d source ~/.venvs/a2d/bin/activate pip install a2d-diary至于版本,我实测用的 v1.4.x,Python 3.9 到 3.12 都能正常工作。a2d-diary 的依赖非常克制,核心依赖只有一个PyYAML(用来解析和生成日记文件的 YAML 头部),可选的增强依赖有rich(格式化终端输出)。如果你安装时提示缺少什么,直接看报错信息补装即可,不会像某些大型框架那样给你拉下来上百个依赖。
装完验证一下版本号,顺手创建一条测试日记,确认基本盘没问题:
a2d --version a2d today "hello, this is my first diary entry"如果终端能正常输出结果,并且~/diary/2025/目录下出现了当月的日记文件,说明安装环节已经畅通。这时候再往下看语法和参数,省得带着报错去理解后面的内容。
3. 语法规则详解:从能用到用顺
3.1 子命令结构:add、today、list、edit、delete 与 view
a2d-diary 的命令行语法走的是绝大多数现代 CLI 工具的模式:主命令 + 子命令 + 若干参数。先记住六个最常用的子命令,你就能覆盖 95% 的日常场景:
| 子命令 | 作用 | 典型用法 |
|---|---|---|
add | 给指定日期新增或追加内容 | a2d add 2025-03-15 "写了一段爬虫代码" |
today | 快速写入/查看今天的日记 | a2d today "明天要交周报" |
list | 按条件列出日记条目 | a2d list --tag work --from 2025-01-01 |
edit | 唤起编辑器修改某一天的日记 | a2d edit 2025-03-15 |
delete | 删除某一条或某一天的日记 | a2d delete 2025-03-15 --yes |
view | 渲染查看某天的日记内容 | a2d view 2025-03-15 --format markdown |
today子命令是最高频的操作,它本质上就是add的一个快捷方式——只不过日期参数自动取当天。这意味着你完全可以只用add完成所有写入,但today能帮你少敲几个字符,积少成多也是效率。
写入的时候,内容参数可以是一次性传入的字符串,也可以通过管道从其他命令接收:
# 直接传字符串 a2d today "完成了用户注册模块的接口联调" # 从管道接收内容 echo "修复了登录态失效的 bug,原因是 Redis key 过期策略配置错误" | a2d today第二种方式厉害的地方在于,它可以和任意命令组合。我经常用这种方式把监控脚本的告警信息直接写入当天的日记,形成一条“异常处理时间线”,后面看回顾复盘时特别直观。
3.2 标签系统与 Markdown 渲染语法
a2d-diary 内置了一套轻量标签语法,规则很简单:在正文里用@标签名的方式标记。比如:
刚开完版本评审会,@work 和 @meeting 都记一下。 晚上去跑步了,@fitness 打卡第 7 天。这里@work和@meeting是两个标签,@fitness是另一个标签。它们会被 a2d-diary 自动解析,并存进 YAML 头部,变成结构化的元数据。之后你用a2d list --tag work就能筛出所有工作中带@work标签的日记。不要小看这一条,标签是日记从“流水账”进化为“知识库”的关键一步,因为检索维度一下子从时间扩展到了主题。
正文内容的渲染语法不用额外学,a2d-diary 直接吃标准 Markdown。你写#标题、-列表、` `代码、**加粗,它都支持。view子命令配合--format markdown,可以在终端里直接渲染成格式化文本;如果配合导出功能,还能生成一篇完全兼容 Obsidian、Typora 等工具的 Markdown 文件。
有个小细节值得注意:标签解析默认只识别@符号后跟着的字母、数字、中文和下划线,遇到空格或标点就截断。所以@work, 今天加班里的标签会被识别为work,而不是work,。这个规则和 Twitter 的用户名分词逻辑很像,简洁且实用。
3.3 管道、交互模式与批量写入技巧
除了直接传参,a2d-diary 还支持交互模式。运行a2d today时如果不带内容参数,它不会直接报错,而是想办法唤起系统默认编辑器(Vi/Vim、Nano,或者在 macOS 上可能是 TextEdit),让你在一个完整编辑器里写长内容。这个设计很适合写大段的深夜感想,或者需要仔细组织语言的复盘总结。
在自动化场景里,管道写法比交互模式更重要。举个具体例子,我写过一个脚本,每天下班前把git log --since="8 hours" --pretty="%s"的输出喂给 a2d-diary:
git log --since="8 hours" --pretty="- %s" | a2d today --no-echo--no-echo参数的意思是写入之后不要立刻回显整篇日记,避免每次脚本运行终端刷屏。工作一天的提交记录变成一条精简的日报,既真实又耗时接近零。
如果你想在脚本里生成多天的日记,可以写循环:
import subprocess dates = ["2025-03-01", "2025-03-02", "2025-03-03"] for date in dates: cmd = f'a2d add {date} "-- 补记的日记内容"' subprocess.run(cmd, shell=True)这个例子展示了 a2d-diary 的开放接口特性——所有功能都能被外部脚本驱动,你的日记系统可以长成任何你想要的样子。
4. 参数体系拆解:把控制权拿回手里
4.1 日期范围过滤与时间维度的高级用法
list子命令最有用的参数是时间范围过滤。很多人在使用初期不知道这两个参数,结果每次都用a2d list输出全部日记再看花眼。--from和--to可以把时间维度精确地切成任意区间,语法非常简单:
# 查 2025 年 1 月整月的日记 a2d list --from 2025-01-01 --to 2025-01-31 # 查最近 7 天 a2d list --from 7d # 查 2025 年所有带 work 标签的记录 a2d list --tag work --from 2025-01-01 --to 2025-12-31关于日期值有一个非常贴心的解析规则:它不仅能识别完整的YYYY-MM-DD,还支持一堆口语化写法,比如yesterday、today、7d(表示七天前)、2w(两周前)、1m(一个月前)。我第一次看到这个设计时感到非常惊喜,因为这意味着你不需要在脑子里把“上个月”换算成具体的日期字符串,直接传--from 1m就完事了。实现上主要有依赖 dateutil 的 relativedelta 做相对时间计算,对闰年、跨月等边界情况处理得都很稳。
时间维度还可以进行聚合统计。使用--stats参数,你可以查看某段时间内的记录条数、标签频次、平均正文长度:
a2d --stats --from 1m输出类似:
时间段: 2025-02-15 至 2025-03-15 日记总条数: 24 标签频次: work(18), fitness(9), meeting(6) 平均正文长度: 142 字符这种统计能力非常适合月末复盘,或者写 OKR 周报时快速了解过去的投入。
4.2 配置文件与参数优先级机制
a2d-diary 并不是把所有参数都压在命令行里,它支持通过配置文件设置默认值。默认的配置文件路径在~/.config/a2d/config.yaml,如果不存在,运行一次任意命令后它会自动创建一个示例配置。
配置文件长这样:
diary_path: ~/diary default_format: markdown default_tags: [] default_editor: vim date_format: "%Y-%m-%d" header_template: 日期: "{date}" 天气: "" 睡眠: ""这里diary_path指定日记文件的根目录,default_editor设置交互模式使用的编辑器,header_template则可以自定义 YAML 头部的字段结构。比如你特别在意健康状况,可以加一行运动: ""作为默认字段,逼自己每天填写。
参数优先级从高到低是:命令行参数 > 配置文件 > 环境变量 > 内置默认值。这意味着,即便你在配置里写了default_editor: vim,真正运行时临时想用别的编辑器,只需要在命令行加一个--editor code就能覆盖配置。这个设计的逻辑和系统环境变量的 PATH 处理一脉相承:配置负责定基调,命令行负责开临时口子。
有一个坑务必注意,就是参数名里的短横线和下划线。a2d-diary 的参数定义走的是 argparse 标准,所以像diary_path这样带下划长的选项,在命令行里对应的参数名是--diary-path。如果你写--diary_path,某些版本会直接包错,而某些版本会通过allow_abbrev机制退而求其次地识别。为了避免玄学问题,我的建议是统一用--diary-path这种短横线写法。
4.3 模板变量与 YAML 自定义字段
模板变量是 a2d-diary 参数体系中容易被忽略但极其出彩的部分。在配置文件里的header_template中,你可以用大括号加变量名的方式引入动态值:
header_template: 日期: "{date}" 星期: "{weekday}" 时间: "{time}"当你运行a2d today时,{date}会被替换成当天的日期,{weekday}会被替换成“星期一”这样的中文星期名,{time}则被替换成当前时间。这个机制让每条日记自动带上丰富的上下文,无需你手动输入。
除了内置变量,模板还支持从环境变量取值,语法是{env:HOME},例如:
header_template: 工作目录: "{env:PWD}"如果你在/home/user/project目录下运行 a2d-diary,这条日记的 YAML 头部就会写上工作目录: /home/user/project。我当时用这个特性做了一个实验:每次记录时把当前命令行所在的目录写进日记,一个月之后回看,所有的工作上下文一目了然,比单纯靠回忆靠谱得多。
YAML 自定义字段最经常配合的用法是后续用脚本处理。比如你想统计自己睡眠时长的变化,就在配置里加睡眠: ""字段,每天顺手填一个数字。之后写个几行的 Python 脚本读取所有日记文件的 YAML,就能画出睡眠趋势折线图。这个思路其实对所有追求量化自我的人都非常实用,而且代码量不会超过 50 行。
5. 实际应用案例:我的三种高频用法
5.1 案例一:开发日志与 git 提交记录自动日报
这是我最常用的场景,也最能体现 a2d-diary 的自动化优势。每天下班前我会跑这样一条命令:
echo "## 今日提交" >> ~/diary/$(date +%Y)/$(date +%m)/$(date +%Y-%m-%d).md git log --since="9 hours" --pretty="- %s (%an)" >> ~/diary/$(date +%Y)/$(date +%m)/$(date +%Y-%m-%d).md但手动敲命令还是太土了。更优雅的方式是写成一个小 Shell 函数放进~/.bashrc(或~/.zshrc):
diary_report() { { echo "## 今日提交"; git log --since="9 hours" --pretty="- %s"; } | a2d today --no-echo }这样每次只需要敲diary_report,当日提交历史就以 Markdown 形式追加进了日记。一周以后,你的日报、周报、月度总结素材全都在那里躺着,写的时候只需要复制粘贴加少量润色,效率提升是很明显的。
配合--stats还能看出自己一周内的工作密度。我试过连续两周统计,发现明显感觉到“忙疯了”的那周,其实 git 提交记录并不多,真正占据时间的是评审和会议。这种数据带来的自我认知修正,比单纯拍脑袋准得多。
5.2 案例二:习惯打卡与标签统计
把 a2d-diary 当作习惯打卡工具的体验也很丝滑。我养成了每天晚上睡觉前写一条打卡日记的习惯:
a2d today "跑步 5 公里,@fitness;读了《金字塔原理》第一章,@reading;没有喝含糖饮料,@health"过一段时间后,用标签统计看看频率:
a2d --stats --tag fitness --from 30d得出的结果可能是:
时间段内 @fitness 标签出现数: 18有了这个数据,就不需要再单独下载习惯打卡 App,数据在自己的文件里,格式完全开放,想怎么分析怎么分析。如果有人想玩得更花,还可以用 Python 读取 YAML 头部写进 SQLite,用 Grafana 之类的工具做可视化看板,但这就属于锦上添花了。
5.3 案例三:与 Crontab 结合定时收集灵感
第三个案例适合脑力工作者。我在终端里跑着一个定时任务,每天上午九点提醒自己写晨间日记,标题模板带有当天的日期和天气占位符,内容留空,晚上再补填,这样既不打断上午的工作节奏,又记得预留位置。
更进阶一点的玩法是用 Python 脚本配合定时任务,把每天某时段收到的输入自动归档:
# collect_notes.py from a2d_diary import Client client = Client() note = input("记录一条灵感: ") client.add(content=note, tags=["idea"])配合cron在固定时间运行:这样可以把日常碎片记录全部收敛到一个地方。虽然用输入函数不太灵活,但至少给了一个跨应用整合的起点,想接 Telegram bot 或 web 表单都行。
5.4 价值反思:日记系统应当为十年后的你服务
用 a2d-diary 一段时间后,我对它的价值有了更深的认识。它不像社交软件那样追求即时反馈,它的回报周期是以月和年为单位的。坚持使用 100 天后,你拥有的不只是一堆 Markdown 文件,而是一条可以拉回任意时间点的主线记忆。
这也是我特别偏爱它的原因。在数据塑造成本越来越低的时代,能有意识地构建自己的时间数据库,并用标准的、可迁移的格式保存,是一种非常理性的信息管理习惯。它的价值在未来,而不是当下。
6. 常见问题与排查技巧实录
6.1 安装后终端找不到命令
这是最高频的报错——你执行pip install a2d-diary一切顺利,但是运行a2d却提示“command not found”。原因几乎总是同一个:Python 的 Scripts 目录没有加入 PATH。
排查方法是先看安装位置:
pip show a2d-diary | grep Location然后检查该目录下的bin或Scripts文件夹是否存在a2d可执行文件。在 Linux/macOS 上,如果路径形如/usr/local/python3.11/bin,就把它加进~/.bashrc:
export PATH="/usr/local/python3.11/bin:$PATH"在 Windows 上,通常是找到C:\Python311\Scripts加进系统环境变量。有些场景下单纯重启终端也能解决,因为部分操作系统的 PATH 刷新有延迟。
6.2 中文乱码问题
终端里查看日记时中文变成一坨乱码,排除日记文件本身的编码问题后,大概率是终端会话的编码不对。在 Linux 下执行:
export LANG=zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8Windows PowerShell 用户则可以先执行:
chcp 65001如果你的日记文件本身保存成了 GBK,那乱码根源在文件编码,不是终端问题。这时需要转换编码:
iconv -f GBK -t UTF-8 input.md > output.md强烈建议从一开始就在配置文件里强制 UTF-8 输出,a2d-diary 本身默认就是 UTF-8,乱码多数是因为操作系统或终端设置背锅。
6.3 --from 7d 查空结果
--from 7d这个相对时间参数看起来很直观,但有些版本对d后缀的解析有限制,如果传7day、7 days这类变体可能无法识别,最终返回空列表。解决方法是查看内置帮助确认当前版本支持的时间单位:
a2d list --help如果确实不支持,兜底方案是直接用 Python 生成精确日期传入:
a2d list --from $(date -d "7 days ago" +%Y-%m-%d)macOS 上的date命令和 Linux 的-d写法略有不同,需要用date -v-7d +%Y-%m-%d。这些细节平时不起眼,但在脚本里一配错就是整套流程白跑。
6.4 YAML 头部解析报错
如果你手动编辑过日记文件,a2d-diary 在读取时可能会报 YAML 解析错误。最常见的原因是冒号后面没有加空格,比如:
日期: 2025-03-15 # 正确写法是冒号后有空格 天气:晴 # "晴"前面没空格YAML 要求键值对里的冒号只能用于分隔,但它后面必须跟一个空格。这不是 a2d-diary 的问题,是 YAML 语法本身的规矩。补上一个空格或引号,解析就恢复了。如果你写了很多行不想手动改,可以用 Python 的ruamel.yaml库做一次自动化修复。
6.5 自动脚本参数传递陷阱
在用 Python 脚本调用 a2d-diary 子命令时,最容易忽略的是内容里包含空格和引号。直接用字符串拼接整条命令很容易因为引号匹配关系出错。正确做法是使用列表参数传递:
import subprocess subprocess.run(["a2d", "today", "这段内容有空格,也安全"])这样就完全不需要担心 shell 二次解析。这个经验适用于任何让你运行外部命令的 Python 脚本,不只是 a2d-diary。
6.6 性能问题:日记多了卡顿怎么办
当日记文件超过几百个、标签数量破千时,a2d list的响应时间会明显下降。这几乎是必然的,因为它是逐个文件扫描而不是走数据库索引。但有一个很实用的优化技巧:利用--from和--to把扫描范围缩小到特定几个月,性能会立竿见影。我现在的日记文件总量超过八百篇,只要按季度查,速度依然很快。
如果你确实需要跨全库搜索,可以写个 Python 脚本把历史 YAML 头数据导入 SQLite,之后查询全部走 SQL,响应基本就是毫秒级。这个进阶方案等你有需要了再折腾,初期别把精力花在优化上。
7. 一点个人的经验总结
我在实际使用 a2d-diary 的过程中,有几点体会想跟想入坑的朋友分享。第一,不要太早追求复杂的自定义模板和参数。刚开始只记流水账,习惯每天写的那一下自然发生了,再去折腾配置。很多人一上来就配了一堆字段,结果坚持不下来,这其实不是工具的问题,是步子迈太大了。第二,标签体系要在用中长出来,而不是一开始设计好。我早期设了十个固定标签,后来发现实际用的只有三个,反而是后来随手加的 @idea 和 @bug 变得最有价值。第三,善用管道写自动化,让日记变成工作流的副产物,而不是给自己增加额外的录入负担。一条优秀的日记轻量到不会打扰你的任何工作节奏,这才是它能长期坚持的原因。
最后分享一个扩展玩法:a2d-diary 生成的 Markdown 文件本身是可以直接放进 Obsidian 仓库拖拽引用的。我用 Telegraf 服务把日记文件和 mySQL 同步了一份,勉强算是拥有了一个可全文搜索的时间摘要索引。这只是个参考,你完全可以按自己的需求把这份日记数据接到其他工具链里。工具是死的,你的想象才是上限。