caveman 这个名字听起来就像石器时代的产物,我却把它当成一个精心设计的个人知识管理项目代号。用这个代号,多少带点自嘲:当我先后折腾过 Notion、Obsidian、印象笔记、Wolai 这些工具之后,真正让我坚持记录、快速找回内容的,竟然是一套没有任何图形界面的纯文本方案。它不依赖特定软件、不要求复杂的数据库、也不需要每个月为同步功能付费,只需要你熟悉几个最基本的能力:文件夹、Markdown 文件和 Git。
你可以把 caveman 想象成一间极简的原始小屋:没有复杂的装修,但每一件摆进去的东西你都清楚知道放在哪里。它适合那些想长期积累知识、又不想被工具绑架的人。它不一定能让你的文字写得更好,但能让你少一些选择焦虑,把精力放回内容本身。这篇文章是我把整套方案从零搭建起来,并稳定用了大半年的完整记录,包括目录结构、核心脚本、常见坑,还有一些只有真正踩过才说得清的细节。
1. 为什么会有 caveman:从复杂工具中逃回石器时代
1.1 我经历过的"工具焦虑"
说实话,我的笔记历史最早可以追溯到印象笔记刚流行那会儿。后来又因为担心数据被绑定,搬到 Bear,再后来觉得 Markdown 更自由,就尝试了 Notion。结果每次打开都要等网络转圈,原本想记一句话,最后变成了等页面加载。更让我崩溃的是,每次换工具都意味着一次大迁移:导出、转换、重新分类,折腾一圈总有一批图片和格式烂在旧工具里。
我逐渐意识到,工具并不会让我的记录变多,反而每一次工具升级都在消耗我的注意力。真正卡住我的不是少一个双向链接,不是少一个 Database,而是我根本不敢开始写。越复杂的工具,需要承担的维护成本越高:要研究快捷键、要维护标签体系、要调整视图布局。这些听起来没什么,但每一个细节都是决策,每一个决策都在挤占写作的能量。
于是我做了一个很"原始"的决定:把笔记系统打回石器时代。我不会再去寻找"完美"的笔记软件,而是亲手搭一个足够简单、足够可靠、几十年后仍然能用的本地记录系统。这个系统不需要联网,不需要服务器,更不需要看厂商的脸色。它只建立在三个永远不会消失的东西上:文件夹、文本文件、版本控制。
1.2 caveman 的核心设计原则
caveman 从第一行代码开始就遵循三个原则。
第一个原则是"纯文本优先"。所有内容都用 Markdown 保存,不引入私有格式、不嵌入二进制对象。图片可以统一放到 assets 目录,但文字本身永远是.md文件。纯文本的好处太多了:任何编辑器都能打开、占用空间极小、搜索速度快、不会因为版本升级而不可读。就算哪天我用不了任何现代软件,只要有一台能显示文字的终端,我的知识库就还在。
第二个原则是"一切皆文件,脚本为接口"。交互入口不是图形界面,而是一个个短小的 Shell 脚本。新增笔记用new.sh,全文搜索用rg,周回顾用review.sh。整个系统没有常驻进程,不占内存,只在你需要的时候执行一条命令。听起来简陋,但恰恰因为简陋,才不会有"某个服务挂了导致用不了"的情况。
第三个原则是"Git 作为唯一真相"。所有历史版本、多设备同步、误删恢复,全部交给 Git 处理。我不需要额外维护一个云同步服务,也不需要担心数据被算法推荐。Git 已经在工程界存在了二十多年,它的可靠性远超任何一款商业笔记软件的同步后台。
这三个原则看起来平淡无奇,但组合起来之后,整套系统几乎不会给你添麻烦。你可以一周不开它,它依然安静地躺在那里,等你想起它的时候,用一条命令就能回到工作状态。
2. caveman 的整体架构与核心设计拆解
2.1 目录结构与笔记命名规范
caveman 的目录结构是这套系统的基础,我把它固定为五个区域加一个脚本目录:
caveman/ ├── 0-inbox/ # 临时捕捉区:速记、灵感、待整理 ├── 1-notes/ # 永久笔记区:整理后的知识条目 ├── 2-projects/ # 项目档案区:按项目组织过程文档 ├── 3-journal/ # 日记与周回顾:时间流记录 ├── 9-assets/ # 附件与图片:笔记引用的静态文件 └── _bin/ # 所有辅助脚本集中存放这样划分的逻辑很简单:没有接收区的工具很难坚持用,因为记录的动作必须足够快;没有永久笔记区,临时内容越堆越多,最终放弃整个系统;项目档案区是为了让某件事形成闭环,从过程文档到总结都能在一个目录内找到;日记区给时间维度;assets 区保证笔记目录不被图片和 PDF 弄乱。
命名规范同样重要。我规定所有 Markdown 文件统一使用YYYYMMDD-短横线标题.md的格式,例如20250115-用stdbuf解决缓存卡顿.md。日期放在文件名最前面有好处:按名称排序时天然形成时间倒序,搜索和归档都方便。短横线代替空格,避免在 Shell 和 URL 中遇到转义问题。如果你需要写得更精确,还可以在日期后加上项目缩写,比如20250115-prd-登录优化.md。
2.2 用纯文本和 Git 解决存储与同步
很多人一开始好奇,为什么不用现成的云笔记软件?这里有一个重要的取舍:同步问题。商业工具把同步做成了黑盒,你永远不知道数据是否完整、是否被审查、是否会被突然收费。caveman 把同步权交还给 Git 本身,多设备之间只是 Git 仓库之间的推送和拉取。
我对比过几类方案的差异:
| 方案类型 | 数据安全 | 离线可用 | 历史版本 | 长期成本 |
|---|---|---|---|---|
| 商业云笔记(Notion/印象) | 依赖厂商 | 部分支持 | 有限 | 订阅费 + 迁移风险 |
| 网盘目录同步(Dropbox/坚果云) | 较高 | 支持 | 依赖版本功能 | 容量限制 |
| 纯文本 + 自建 Git 仓库 | 完全自主 | 完整支持 | 每次提交都可回溯 | 几乎没有 |
选择 Git 还有一个额外收获:提交信息就是你的复盘时间线。我习惯在每天结束时执行git add . && git commit -m "2025-01-15 review",一个月后回看提交记录,就能清楚地知道那段日子自己在关注什么。这种元数据是商业软件给不了你的。
2.3 核心脚本的功能设计
caveman 的日常使用主要依赖四个脚本。
new.sh:新建笔记,自动用日期拼文件名、写入模板、打开编辑器。search.sh:基于 ripgrep 实现全文搜索,并支持按目录和标签过滤。review.sh:周回顾脚本,列出本周新增与修改的笔记,生成回顾清单。tasks.sh:扫描所有待办标记(如- [ ]),生成待办事项列表。
四个脚本共享同一个根目录配置,所以第一次使用时只需要修改脚本顶部的CAVE_ROOT变量。脚本都放在_bin/里,并把这个目录加进 PATH,然后用短命令调用。比起安装一个 App,这种"手工拼装"的方式多了一点仪式感,但也多了一分可控感。
3. 从零搭建 caveman:实操全过程
3.1 初始化项目骨架
先在目标位置创建目录结构,并初始化 Git 仓库。
mkdir -p ~/caveman/{0-inbox,1-notes,2-projects,3-journal,9-assets,_bin} cd ~/caveman git init接着添加一个README.md,把系统约定写清楚,包括目录用途和命名规则。这一步对长期使用非常重要,否则过段时间你自己都会忘记当初的设计。
touch README.md建议在 README 里写一段简单的使用说明,然后做第一次提交。
git add . git commit -m "init caveman"到这里系统骨架已经成立。注意:如果你在 Windows 上使用,建议开启 WSL 或用 Git Bash,因为后面的脚本都是 Bash 语法。macOS 和 Linux 则开箱即用。
3.2 编写新增笔记的脚本
先把_bin/new.sh创建出来。这是整个系统里使用频率最高的命令,它会自动生成带日期的文件名,并打开你默认的编辑器。
#!/usr/bin/env bash set -euo pipefail CAVE_ROOT=~/caveman TARGET_DIR="${2:-$CAVE_ROOT/0-inbox}" TODAY=$(date +%Y%m%d) TITLE="${1:-untitled}" SAFE_TITLE=$(echo "$TITLE" | tr ' ./:' '_') FILE="$TARGET_DIR/${TODAY}-${SAFE_TITLE}.md" if [ -f "$FILE" ]; then echo "已经存在:$FILE" exit 1 fi cat > "$FILE" <<EOF # ${TITLE} Date: $(date +%Y-%m-%d) ## 内容 EOF ${EDITOR:-vi} "$FILE"我解释几个关键点。
set -euo pipefail是写给 Shell 脚本的"安全带",遇到错误直接退出,避免在错误树上继续执行。SAFE_TITLE把标题中的空格、斜杠、冒号都转成下划线,这样文件名不会因为包含特殊字符而引发问题。${EDITOR:-vi}表示优先使用你自己设置的EDITOR环境变量,没有就回退到 vi。
使用时,在caveman目录下执行:
_bin/new.sh "用fzf做笔记搜索"它会在0-inbox/生成一个类似20250115-用fzf做笔记搜索.md的文件,并打开编辑器。如果你希望直接写进某个分类目录,可以传入第二个参数:
_bin/new.sh "Logseq数据库结构分析" ~/caveman/1-notes注意:这里的路径是为了当前操作临时指定,实际项目中建议把所有新笔记默认放到 inbox,整理时再移动,避免还没想好分类就陷入纠结。
3.3 实现快速检索与标签过滤
笔记系统成败的关键在于能不能快速找到内容。我选择 ripgrep 作为搜索引擎,它比 grep 快很多,而且默认忽略二进制文件。
最简单的全文搜索命令是:
rg -n "关键字" ~/caveman-n显示行号。如果只想看某个目录,就把路径缩小到1-notes或2-projects。我习惯再加一个--type md排除非 Markdown 文件:
rg -n --type md "关键字" ~/caveman如果笔记里有标签,比如#编程,也可以直接搜标签:
rg -l "#编程" ~/caveman/1-notes-l只输出文件名,不输出匹配行,适合用于批量重命名或进一步筛选。
为了更顺手,我把搜索命令封装到_bin/search.sh里,并加入目录参数:
#!/usr/bin/env bash set -euo pipefail CAVE_ROOT=~/caveman SEARCH_DIR="${2:-$CAVE_ROOT}" rg -n --type md "$1" "$SEARCH_DIR"之后用_bin/search.sh "API设计"一条命令完成任务。如果你喜欢交互式选择结果,可以配合 fzf:先让 ripgrep 输出文件列表,再用 fzf 选定文件后用编辑器打开。这种方式在笔记数量很大时尤其好用。
3.4 把周回顾变成例行公事
笔记只输入不整理,很快会变成一堆文字垃圾。我给自己设定了一个轻量循环:每天记录,每周回顾。周回顾用脚本完成,把本周新增和修改的笔记列出来,生成一个回顾模板。
#!/usr/bin/env bash set -euo pipefail CAVE_ROOT=~/caveman cd "$CAVE_ROOT" echo "=== 本周新增/修改笔记 ===" find 0-inbox 1-notes 2-projects 3-journal -type f -name "*.md" -mtime -7 | sort注意find的时间参数在 Linux 和 macOS 上有差异。Linux 的 GNU find 支持-mtime,macOS 的 BSD find 也支持,但如果你想精确到"最近一周",更稳妥的是用-mtime -7。如果你的系统不支持,也可以用:
find . -name "*.md" -newermt "$(date -v-7d '+%Y-%m-%d')" 2>/dev/null || \ find . -name "*.md" -newermt "$(date -d '-7 days' '+%Y-%m-%d')"这段命令在 macOS 上用date -v-7d,在 Linux 上用date -d '-7 days',哪个成功用哪个。我在脚本里默认保留这一行,省去手动判断的麻烦。
周回顾时打开生成的清单,每一条问自己三个问题:这条笔记值得保留吗?如果需要保留,放进正确目录了吗?如果已经整理过,要不要更新一个小结?完成之后把清单作为当天日记的附件内容合并进3-journal/,然后提交一次 Git。
4. 用 caveman 的真实工作流示例
4.1 一个产品经理的日常工作流
假设你是一个产品经理,白天排满会议,零碎信息来得非常快。使用 caveman 的方式是:会议中听到任何需求、用户反馈、竞品细节,先立刻记到0-inbox/。这一步不要求格式,一句话或者几个关键词都行,关键是"先存下来"。
下班前,你抽出十五分钟执行一次整理。把依然相关的需求移动到2-projects/需求调研/,把可复用的认知归入1-notes/。第二天早上打开项目目录时,你会看到前一天的会议重点,而不是满屏乱飞的零散便签。
等到一周结束,运行_bin/review.sh,你就能清楚看到这周推进了哪几个项目、哪些想法还悬在半空。这种"记录-整理-回顾"的节奏,正好对应产品工作中常见的"发散-收敛-复盘"循环。不需要开额外的东西,就把工作流和知识流统一到了同一个底层。
4.2 一个程序员的技术笔记流转路径
程序员这个群体其实最应该感谢纯文本。我处理技术笔记的路径是:遇到一个 bug,先用最原始的手段定位问题。有人会调侃这是 "caveman debugging",用 printf 打印日志、用二分法注释代码,慢慢逼近问题根因。事实上,这种看似笨拙的方法恰恰是最可靠的调试方式,因为它不依赖任何调试器魔法,每一项输出都来自你自己的观察。
排查过程中的每一步尝试,我都会顺手记到0-inbox/。定位到根因后,写一篇1-notes/笔记,比如20250115-解决跨平台换行符问题.md。笔记里包括现象、原因、解决方案、验证方法,甚至可以把问题复现脚本也放进去。
这些笔记会在未来的某一天救你一次。当处理过的临时代码被翻出来,或者接到一个结构相似的 bug 时,你不再需要从头排查,直接_bin/search.sh "换行符"就能找到当时的完整记录。这种"自己的知识库"比任何收藏夹都有用。
4.3 手机上也能用:移动端同步与随手记
纯文本方案离开电脑也能工作。我通常在手机端保留一个只读副本,随手查看和分享。方法很简单:把 caveman 目录推送到一个私有 Git 仓库,手机上安装一个支持 Git 的编辑器客户端,拉取最新版本,用手机编辑再推送。
如果平时不在外面写长文,只记录灵感,也可以不用 Git,直接把0-inbox/单独同步到手机。例如用 Syncthing 做局域网或点对点同步,整个过程不经过第三方服务器。走到哪记到哪,回到电脑前再统一整理。
这里有一个经验:手机端尽量少做深度整理。屏幕小、输入效率低,只适合快速追加内容和查资料。真正的整理、归纳、沉淀,一定在电脑上完成。移动端和桌面端各司其职,才不会让同步变成负担。
5. 常见问题与排查技巧实录
5.1 中文文件名乱码问题
纯文本虽然简单,但中文环境下的乱码问题很常见。如果发现文件名显示乱码或者搜索时中文搜不到,多半不是内容损坏,而是系统 locale 设置不对,或者某些文件被 Windows 编辑器保存成了其他编码。
第一件事是通过file命令检查文件编码:
file 20250115-测试.md正常输出应该包含UTF-8。如果是ISO-8859或者ASCII,说明编辑器可能用了非 UTF-8 保存。可以在脚本开头强制设置 locale:
export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8在 macOS 或 Linux 上,这样就能保证find、sort、rg对中文按正常字符顺序处理。Windows 用户使用 WSL 时也建议在~/.bashrc里加上这两行。
5.2 文件名带空格导致脚本失灵的坑
我最初写new.sh时,直接用$1.md生成文件名,结果标题里一出现空格,整条命令就断裂。为什么会断?Shell 默认按空格切分参数,未被引号包裹的变量会被拆成多个参数。
例如:
# 错误写法 FILE="$TARGET_DIR/`echo $1`/$TODAY-`echo $1`.md"一旦$1包含空格,FILE的值里就会带着空格,后续cat > $FILE和editor $FILE都可能读错路径。解决办法有两个:文件名级别转义空格,变量引用加引号。
我用tr ' ./:' '_'把所有空格、斜杠、冒号转成下划线,文件名里干脆不出现空格。同时,所有命令里的"$FILE"都加上引号,确保万一文件名里仍有空格也不会被拆开。这个习惯要写进脚本的每一处,否则换到其他脚本时很容易重新踩坑。
5.3 Git 冲突与多设备同步
多设备都提交、推送之后,你偶尔会遇到 Git 冲突。比如电脑上改了一篇笔记,手机端又改了同一个文件,拉取时就会冲突。第一次遇到时不要慌,这是正常的版本控制行为。
我的处理方式很简单:打开冲突文件,找到<<<<<<<、=======、>>>>>>>标记之间的内容,手动选择保留哪一边,然后用git add标记解决,再提交合并。
更重要的是一开始就减少冲突的概率。我养成了一个习惯:每台设备只看重某一部分内容。电脑上操作1-notes和2-projects,手机上只追加0-inbox。划分清晰的职责后,同一时间编辑同一个文件的概率会低很多。另外,任何设备在开始工作前先git pull,结束后及时git push,不要攒一天的变更。
5.4 临时笔记堆积成山怎么办
很多人使用类似方案时,会死在 inbox 堆积上。我大概在第二个月就遇到了这个情况。看到0-inbox/里躺着几十条"当时觉得重要、现在完全想不起来"的笔记,动力瞬间少了一半。
后来我给自己定了一个规则:每次新建笔记时问自己一句,"这条内容就算 48 小时之后再看,还会重要吗?"如果不确定,也照记不误。真正解决问题的是每周日的定向清理,我会把所有超过 30 天没有修改的 inbox 文件列出来统一归档:
find 0-inbox -type f -name "*.md" -mtime +30 | sort这条命令把陈旧内容全部暴露出来。我通常做三个动作:内容有长期价值的移动到1-notes/,属于某个项目的移动到2-projects/,其余的直接删除。删除也是一种整理,敢于删东西的笔记系统才可能保持清爽。
6. 实战心得与进阶扩展
6.1 坚持用了半年后,我留下的几个习惯
用 caveman 的时间越长,越能体会到"简单的系统需要配合理的习惯"。我保留了三个习惯,它们共同撑起了整个工具的价值。
第一,每天至少记一条。不一定是长篇大论,哪怕只是"今天在电梯里想到一个隐藏菜单"这种半句话也行。持续记录能让你对灵感保持敏感,也能让周回顾有足够素材。
第二,移动内容时顺手改文件名。如果一篇笔记从 inbox 移到1-notes/,我会顺手把它从临时标题改成更清晰的YYYYMMDD-主题.md。这会倒逼自己想清楚:这条知识到底应该被什么标题召唤出来。
第三,每周日固定跑一次review.sh。这不是向 KPI 看齐,而是给自己一个"放下这一周"的仪式。看到这一周的输入被分类、归档、提交,脑子里会有一种轻快感。哪怕这周产出的内容不多,至少知道自己没白忙。
6.2 可以继续扩展的方向
caveman 目前做的是纯文本记录,但它的骨架可以继续长出更多能力。
比如任务管理:在笔记中用- [ ]标记待办,再写一个脚本扫描所有目录生成任务清单,就能变成一个极简 GTD。再比如发布博客:Markdown 文件本来就能被静态站点生成器消费,加一层模板,就能把私有笔记导出成对外博客。如果你想加全文索引,可以用sed和sort生成词频统计,也可以慢慢引入更重的检索工具。
我目前还没有给系统加插件体系,因为每加一项功能,都要保证它不破坏"原始感"这个核心价值。如果你有能力维护更复杂的代码,扩展是件很自然的事;如果你的目标是持续记录,那就保持现在这个状态也已经足够。
6.3 最后想说的话
有人可能会嘲笑这种方案太笨,甚至不像这个时代该有的"知识管理"。但我用了大半年后最大的感受是:越原始的工具,越难被淘汰。你不需要在每次版本升级之后重新学习操作逻辑,不用担心哪天厂商停服、数据迁移有多痛,唯一需要维护的只有你自己的习惯。我甚至觉得 caveman 这个名字应该反过来理解:不是原始人用上了现代工具,而是现代人终于学会像原始人一样珍惜自己稀缺的注意力。
如果你也正在为工具感到焦虑,不妨从今天的日期和一个小文件夹开始。给第一篇文章起一个准确的名字,然后一门心思写下去。也许不到一周,你就会被这种简单直接的方式打动。