caveman 是我最近一直挂在嘴边的一个小项目代号,外号叫“穴居人方案”。圈子里偶尔看到叫 caveman 的工具或开源项目,思路大多差不多:把知识管理这件事打回原形,不用花哨的 App,不用云端数据库,就用文件夹、纯文本和几个简单脚本,搭一套能扛十年、完全属于你自己的资料系统。我花了一个周末从零把它跑起来,随后连续用了一个季度,今天把整套过程和踩过的坑都摊开讲一遍。
这个方案解决的核心问题是:笔记类工具越做越重,数据锁在别人平台上,本地不想装全家桶,又希望所有内容能长期保存、随时检索、可自动备份。如果你也受够了各种“笔记软件全家桶”,或者对隐私和长期可迁移性有执念,那按下面这套流程走下来,你会得到一个只属于你的纯文本知识库,整个过程大概只要一个小时。
1. 为什么我会做“穴居人”这套东西
1.1 现代知识管理工具的普遍痛点
大概三年前,我还在老老实实用商业笔记软件。一开始体验确实好:界面好看、多端同步、文件夹随便建。但用得越久,问题越明显。最难受的是数据锁死。某天想把笔记批量导出,发现只能按册导出成 HTML 或者它自己的格式,想迁移到别的工具几乎等于重新整理一遍。更别说离线访问要单独装客户端,后台动不动要登录,隐私上心里那道坎一直过不去。
后来我切换成了所谓“第二大脑”类工具,数据库本地存、也有一些开放性。但这类工具的问题在于太重。打开一个界面要等半天,写一条临时想法要经过一堆概念和流程插槽,等你想从里面找一个半年前随手记的灵感时,反而比用文件系统的搜索还麻烦。对我这种技术背景的人,它把简单的事情复杂化了。
于是我开始反思:知识管理最本质的需求其实只有三个。第一,能快速记录;第二,能快速找到;第三,资料不会丢。这三个需求,其实 1970 年代的设计就解决了,就是文件系统、纯文本和版本管理。我把这个思路戏称为“穴居人思维”:不追求花哨工具,像穴居人一样用最原始、最可靠的东西,反而是最耐用的方案。
1.2 为什么是“caveman”而不是某个商业软件
“caveman”这个词听起来糙,但恰好说明了这套方案的核心精神。穴居人不会担心自己的火种被某个平台停服,不会被“云同步”绑架。一个文件夹在手,所有数据都是你的;一个 Git 仓库在手,所有历史版本都是你的;一个 grep 在手,所有内容都能被搜到。
这个方案不是为了否定现代工具,而是为了找回简单可控的感觉。每个文件都是标准 Markdown 纯文本,没有私有格式,任何设备、任何系统打开都不会乱码。文件夹的结构就是分类体系,你可以按自己的习惯建立层级,不用学习软件的“自定义标签”“双向链接”“数据库视图”这些概念。它是反对工具崇拜的:工具越通用,越经得起时间考验;方案越简单,越难出故障。
这个思路适合什么样的人?技术从业者、插件控、隐私敏感用户、长期写作者、自建博客玩家,以及所有被商业笔记软件坑过的人。它不适合需要多人实时协同、离不开富文本排版、或者图片素材特别多还需要在线管理的人。认清这个边界很重要,后面很多问题其实都是边界问题。
2. 核心设计思路:把文件夹当数据库用
2.1 用目录结构代替数据库表
caveman 的设计起点非常朴素:把你的笔记库想象成一台文件柜,而不是一个数据库。这台文件柜只需要几个抽屉。
~/caveman/ ├── daily/ # 日记和日志,按日期命名 ├── notes/ # 常青笔记,主题性内容 ├── inbox/ # 临时想法收集箱 ├── assets/ # 图片、PDF等附件 └── README.md # 使用说明和索引文件为什么用这四个目录而不是更多?因为需求膨胀会拖垮维护意愿。daily 放时间流内容,notes 放主题性内容,inbox 是应急口,assets 放二进制文件。如果分类太多,记录一个东西前要纠结“该放哪”,几次下来就不想用了。四个目录意味着十五条规则,足够覆盖我百分之九十九的使用场景。
目录里所有的记录都是用日期加短标题作为文件名。例如2024-07-09-搭建博客的流程.md或notes/nginx-cool-timeout.md。日期前缀带来了几个意想不到的好处:文件在系统层面天然按时间排序;重名概率极低;文件名本身就是元数据,不需要特地在正文维护写作时间。标题要是没有,就写untitled.md,记下来再说,内容永远优先于分类。
2.2 纯文本格式与 front matter 的必要性
正文一律用 Markdown 纯文本,不引入任何私有格式。有人会问,要不要像某些工具那样在每个文件头部写 YAML front matter(就是三横线夹着title、tags、date那种)?我的经验是:可以,但能少就少。因为我经常用手机或其他脚本直接读取文件,头部信息越少越不容易出错。
我最终只保留了一个可选字段:tags。其他信息都交给文件名、目录层级和 Git 提交历史去承担,因为这三样东西本身就足够可靠。比如正文首行标题就是索引标题,不需要在 front matter 里重复维护一份。如果你想加标签,写在文件头部或者直接写成内联#标签都可以,检索的时候统一用rg去匹配。
这里有个生活化类比:文件系统是最原始的“图数据库”——目录是父子关系,发往同一处的文件可以互相同步,全文搜索是即时索引。你不需要提前设计复杂的表结构,文件本身就是行,目录树本身就是索引,搜索就是全表扫描。数据量只有几万个文件时,全表扫描完全够快,真的不用焦虑性能问题。真到了那个规模,你盘一下自己的十年笔记量,就会发现这完全不是瓶颈。
2.3 为什么我坚持不用数据库和云服务
caveman 最核心的设计判断,就是不引入数据库、不把数据托付给第三方云服务。原因有三层。
第一,数据所有权。数据库的底层是特权格式,换工具等于转换数据。而纯文本文件夹只依赖操作系统和文件编码标准,这些基础技术不会被某一家公司淘汰。第二,可组合性。文件可以被任何工具读写,Git 可以做历史版本,rsync 可以做备份,SSH 可以远程访问,甚至以后你不想用我的脚本了,自己拿文件管理器操作都可以。第三,故障排查难度低。数据库出问题很难修,文件系统出问题可以用基础工具和备份恢复。方案越是依赖简单的机制,系统崩溃时你需要的救生员越少。
当然,这不意味着与云完全绝缘。我依然用 Git 实现异地备份,把它推到一个私有远程仓库,用 GitHub 或任何你喜欢平台都可以。但这里的逻辑不同:云只是备份存储介质,不是数据的法定监护人。真出事的时候我不需要从某个平台“导出”,只需要git pull就能拿到完整的历史记录。这是一个巨大的心理差别,也是这个方案真正让人安心的地方。
2.4 三个核心命令:尽力避免发明轮子
caveman 没有做成一个庞大的软件,核心就是三个 shell 脚本,或者三个函数:caveman_new(新建笔记)、caveman_find(搜索打开)、caveman_sync(同步备份)。我把它们封装成 Bash 函数,放在~/.bashrc或~/.zshrc里,全 shell 通用,零依赖的情况下也能退化成find加grep。
为什么只做这三个?因为一条知识从产生到复用的闭环就是:记录 → 找到 → 备份。任何编辑、阅读、发布、统计功能都可以用现成工具挂接上去,不必为每个功能自己造轮子。交互形态上,我刻意没有做图形界面,直接用终端和快捷键,因为快是工具的第一美德。
插一句,很多人会觉得脚本方案“简陋”,不如图形软件美观。但美观应该建立在数据组织方式上,而不是皮肤上。我的数据是开放格式,今天用命令行,明天如果觉得不舒服,写个 Web 界面套在这些文件上也就是半个晚上的事情。底层稳了,上层随便换,这种架构上的灵活性,封闭生态给不了。
3. 从零把 caveman 跑起来:完整实操流程
3.1 初始化仓库:十分钟搭好底子
下面这套流程,是在 macOS 和 Linux 上都验证过的,Windows 用户可以用 WSL 或用 Git Bash 模拟。第一步,在用户目录下创建项目文件夹并初始化 Git 仓库:
mkdir -p ~/caveman/{daily,notes,inbox,assets} cd ~/caveman git init echo "# Caveman" > README.md git add . git commit -m "init"这个过程做了几件重要的事:mkdir创建四个顶级目录;git init让整个笔记库从此有了历史版本管理;README.md作为知识库的“引导页”,我会在顶上写一句话提示自己“先搜再写、先记再理”。顺手git commit保证初始状态干净。
这里你可能会问,为什么assets目录也要纳入 Git 管理?如果一个笔记里的配图只在本地,备份的时候就会缺块。所以要把二进制文件一起放进去。真正的大文件另说,后面会有归档策略。初始化完成之后,你已经有了一台空白的“穴居人知识库”数据目录,接下来给它装上记录入口。
3.2caveman_new:建立新笔记的正确姿势
新笔记的入口脚本,核心逻辑是:生成日期前缀文件名,创建空内容,然后自动打开编辑器。我在~/.bashrc里加了这样一段函数:
caveman_new() { local dir="$HOME/caveman" local date=$(date +%Y-%m-%d) local title="${1:-untitled}" local slug=$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-') local file="$dir/notes/${date}-${slug}.md" if [ -e "$file" ]; then file="$dir/notes/${date}-${slug}-$$.md" fi if [ ! -f "$file" ]; then touch "$file" fi ${EDITOR:-vim} "$file" }使用方式就是caveman_new 我的新想法,它会自动落到notes/2024-07-09-我的新想法.md,并打开 vim。如果你只想快速记一句临时内容,用caveman_new tmp然后写几行就行,文件名不完美没关系,以后可以用mv改。
这段代码里有一个细节:tr -cd 'a-z0-9-'是为了剔除文件名里的特殊字符。我踩过名字里带斜杠、冒号的坑,会造成目录层级错乱或者 Windows 不兼容。虽然 UTF-8 和中文系统下中文文件名没问题,但为了跨设备稳定,我还是把文件名的主体转成英文短横连接形式。正文里的标题随便中文,文件名只负责唯一性和排序,这样检索和迁移都非常省心。
3.3caveman_find:秒级全文定位
记录只是开始,更关键的是找到。我在所有依赖都安装的环境下用的方案是基于fd+fzf,这两个工具分别负责快速发现文件和模糊搜索:
caveman_find() { local file file=$(fd . "$HOME/caveman" -e md -e txt | fzf --preview 'bat --style=numbers --color=always {} 2>/dev/null || cat {}') [ -n "$file" ] && ${EDITOR:-vim} "$file" }fd的作用是快速把所有 Markdown 文档路径列出来,fzf提供一个交互式模糊匹配窗口,文件的同时会实时预览。选中的文件直接用$EDITOR打开。如果想按内容搜关键词,再加一层管道:
caveman_grep() { rg --color always -l "$1" "$HOME/caveman" -g '*.md' | fzf }这一条命令会先全文搜索出包含关键词的文件列表,再进入 fzf 选择。纯文本的好处在这类场景里特别明显,数据散落在不同目录也不怕,把搜索范围统一指向整个知识库根目录就行。
如果没装 fd/fzf,完全可以用系统自带的find和grep替代。只要你愿意,用文件管理器的搜索框一样能搜到。所谓方案稳定,说的就是即使工具链退化到原始状态,核心流程也不会瘫痪。
3.4caveman_sync:自动备份与多机同步
数据安全靠的是备份意识,自动化让对象永远不用想起它。sync 脚本的职责只有两件事:把本地修改提交到 Git 仓库,再把远程仓库的最新状态拉下来合体。这是我在.bashrc里的版本:
caveman_sync() { cd "$HOME/caveman" || return git pull --rebase --autostash git add -A git commit -m "sync on $(date '+%Y-%m-%d %H:%M:%S')" --allow-empty git push }这段脚本的思路是:先拉去远端最新内容并自动变基,把本地有而远端没有的改动临时藏起来,New 调整之后再释放。在大多数单机使用场景下,这个流程足够保证两台设备之间不产生严重冲突。--allow-empty保证即使没有任何修改也要建立新的 commit,这样每次同步都会留下一个时间戳,复盘写到哪一阶段更容易。
有人问为什么不挂 cron 定时跑?我两个方式都试过。定时同步适合固定设备,比如家里台式机;但笔记本经常睡眠,网络也不稳定,定时任务经常错过或者报错。最终我保持手动 sync 加上事后几分钟补跑的习惯,偶尔忘记了,下一次 push 会把历史补齐,不会丢内容。关键的教训是:同步不等于备份,三个月至少把一个完整 Git 仓库打包放到外部硬盘或对象存储一次,这才是保底的备份方案。
3.5 可选的发布能力:一键生成静态站点
纯文本的最后一块拼图是发布分享。有时候我希望把一些笔记公开成网页,给朋友看或者自己远端访问。caveman 这次也顺便做了一个 30 行左右的 Python 小工具,把所有 Markdown 转成单个静态 HTML 目录,整个网站所有页面都是相互链接的。
核心流程:用 Python 的markdown库把.md转成 HTML 片段,再套进一个简单模板(含导航、时间、标题),输出到~/caveman/site/。然后我用 Git 或 rsync 把这个site/目录推到多个托管平台。这种方法把“笔记库”和“博客发布”合并成同一个知识库,写完即发布,不需要 CMS 后台。
当然,纯手工用pandoc也一样可以:pandoc file.md -s -o file.html。只是成批转换还是脚本方便。这部分是锦上添花,没有它不影响核心闭环。
4. 一路踩过来的坑:常见问题与排查实录
4.1 中文文件名与编码问题
纯文本方案最怕的就是字符编码和文件系统兼容性。我一开始新文件名直接用中文标题,当时在 macOS 没问题,但同步到旧设备时出现乱码;Windows 上甚至有些字符直接被拒绝作为文件名。最终的处理方案正如上文说的:文件名只用英文短横格式,正文标题保留中文。如果搜不到、找错了文件,第一反应先查看编码,统一文件编码和换行符为 UTF-8 与 LF。
在 Git 层面,可以设置git config core.quotepath false,这样 git status/commit 记录能看到中文文件名,而不是转义成\xxx的形式。别小看它,排查问题的时候看到可读的文件名比看编码要快得多。
4.2 移动端怎么处理
caveman 最弱的一环是手机。因为整个库是文件夹结构,手机上的文件 App 虽然能浏览和编辑,但体验不如原生笔记软件。我的解决方案是折中:手机上放一个只看不写的目录,用 Syncthing 之类的文件同步工具把整个知识库同步过去;偶尔有必须记录的内容,先写到手机里的一个inbox/便捷记录.txt,回家跑一次caveman_sync,再手动归位。对技术人来说这个闭环完全可以接受。
如果你真的需要在线编辑,也可以把整个 Git 仓库推到一个私有仓库,手机上用支持 Git 的编辑器改完再提交。但我不推荐作为主流程,因为移动端输入效率低,重内容还是要在电脑上沉淀。
4.3 文件数量大了会不会卡
我目前的知识库接近两千个 Markdown 文件,fd和rg的响应都在毫秒级。按每天两篇算,十年也才六七千文件,性能压力不大。真正让我卡的是把大量图片也塞进了 Git 仓库,一个几十 MB 二进制文件就会拖慢 pull/push。处理方法很简单:assets里只保存小图,大文件丢到网盘或对象存储,单独在 README 里留链接和说明。
如果未来真的到了上万文件级别,不需要重构整个方案,只需要按年份归档:notes/2024/、notes/2025/,搜索时带时间范围即可。文件系统的弹性往往超出想象,系统瓶颈不在文件数,而在你检索的时候有没有准确的关键词。这时候长期精心写文件名和正文首行就显得特别值钱。
4.4 Git 冲突和误删恢复
用 Git 做同步,不可避免会遇到冲突。虽然--rebase --autostash让大部分场景自动完成,但真冲突来的时候,还是要在终端处理。我自己的经验:冲突往往发生在同一文件在桌面包和笔记本上都有改动的情况。解决方案是避免“同一时段双端同时改同一个文件”,实在不行打开冲突标记手动选择内容。
误删文件是最常见的事故。好在我每次 sync 都 commit,恢复只需要一行代码:
git log --oneline --all -- <被删文件路径> git checkout <commit-hash> -- <被删文件路径>比起任何笔记软件自带的“删除备份”,这种版本管理语境下的恢复操作永远可用,哪怕文件已经被删三个月,只要 commit 还在就能找回来。这也是纯文本方案最值得宣传的福利之一。
4.5 编辑器与换行的统一
打包前最后一个坑:跨平台的换行符和编辑器差异。Windows 的记事本会默认用 CRLF,导致 Git 提示文件被修改,甚至造成渲染异常。我的解决方案是全局统一:git config core.autocrlf input。编辑器统一用 vim/neovim 或者星门编辑器(VS Code),保存时强制启用 “End with newline” 和 “Trim Trailing White Space”,这能让 diff 和全文搜索少一堆噪音。
4.6 问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 文件名乱码 | 文件名非 UTF-8 | 文件名统一英文短横,设置core.quotepath false |
| 手机端打不开/不便编辑 | 文件数多,工具慢 | 手机用同步工具,不在此端创作 |
| Git 大量文件冲突 | 双端同改一文件 | 避免双端同时编辑,手动解决少量冲突即可 |
| push/pull 很慢 | 仓库塞了大二进制 | 大文件移出 Git 仓库,用外链引用 |
| 搜索到文件但打开失败 | 编辑器路径或默认设置错误 | 检查$EDITOR环境变量,确认文件路径在本地存在 |
| 同步时发生 rebase 错误 | 本地有未提交改动 | 先git stash或手动提交,再 pull 后处理 |
这个速查表是我实际使用中的经验浓缩,比任何文档都管用。如果你是自己的方案,建议同样维护一份自己的速查单,随手记下踩过的坑,回头翻很值得。
5. 使用一个季度后,我对 caveman 的真实体会与扩展方向
真正上手用了一百天后,我的感受可以浓缩成三点。第一,检索变成了一种乐趣。任何时候想找一个半年前的灵感,终端里caveman_find然后敲几个关键词,几乎零等待。过去我用图形笔记软件想搜一个笔记,要先等客户端加载,再看着转圈,像开盲盒一样。第二,写作的摩擦被降到最低。起个文件名,打开 vim,正文直接敲,没有字体弹窗,没有排版工具栏,让我非常专注于内容本身。第三,心里踏实。所有数据躺在本地,备份一次就是 git push,十年后平台还在不在未知,但我这些.md文件比我存过的大部分云盘活得都长。
当然它也不完美。多人协同场景完全没有,富文本、表格、日历视图也不存在,连手机端都只能勉强看和轻量改。这个方案的代价是把很多需求和审美转移到自己的知识和脚本里,如果你是团队协作型选手,它并不合用。但如果是个人知识管理,这套方案的收益远大于代价。
后续扩展的方向我列在下面,读者可以按自己的需求挂接。
- 日记模板:
caveman_new daily支持生成当天日记的模板(天气、心情、三件要事),文件落到daily/2024-07-09.md。 - 书摘和链接库:每条资料存一个
notes/book-书名.md,头部写书籍元数据,正文放摘抄、评论、引用链接。 - 记账:用 TSV 文件存记账记录,配合 Unix 工具做月度汇总。这不是 Excel 的平替,但秒开秒查。
- 发布博客:使用前文提到的静态站点生成脚本,把
notes中带public标签的笔记发布成站点。 - 任务管理:把任务文件丢进
inbox,每天写下“今天要做的”,用待办勾选语法标记。
我个人在实际操作中的一个小小体会是:不要一上来就把自己这套方案设计成一个大平台。从一个目录、一个脚本开始,用一周记录自己的想法,再逐步加功能。等用顺手了,工具和方法论会自然长成你自己的形状。最稳定的系统不是我写的代码,而是这个习惯本身——不管工具多简陋,只要你肯打开它写几行字,它就是最好用的工具。
最后想给大家一句忠告:方案的核心不是技术,而是你对资料的长期主义。穴居人方案的魅力不在脚本,在于把复杂归还给简单,把数据交还给你自己。