ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

caveman:一个零依赖的极简命令行笔记工具,用纯文本和 grep 管理你的第二大脑

caveman:一个零依赖的极简命令行笔记工具,用纯文本和 grep 管理你的第二大脑 caveman我写了一个连“穴居人”都会用的零依赖命令行笔记工具如果你和我一样试过 Notion、Obsidian、Bear、Joplin最后发现笔记越记越少、工具越换越累那这篇文章应该能给你一点启发。我花了几天时间写了一个叫caveman的极简命令行笔记工具逻辑简单到可以用一句话说清楚所有笔记都是带时间戳的纯文本文件靠grep和文件夹来管理整个工具就是一个不到 200 行的 Bash 脚本。为什么叫这个名字因为我希望它干脆、原始、够用——那种“穴居人看了一眼就会用”的原始感。这个工具适合什么人呢如果你天天泡在终端里习惯用 Vim、SSH 到服务器上工作或者就是受够了各种客户端的同步问题、按文件夹分类的强迫症焦虑那caveman大概率能戳中你。它没有数据库没有云同步没有富文本编辑器甚至连配置文件都只有一行可选的环境变量。你只需要保证系统里有 Bash 4.0 以上剩下的全交给纯文本和文件系统。我不打算劝你卸载手里的笔记软件。诚实的说法是caveman是我的“第二大脑”里最底层的那层抽屉它干的是那种“三秒钟记下来、五秒钟找回来”的脏活累活。下面我把完整的设计思路、核心实现和一些踩坑经验都摊开讲你可以直接照着抄也可以把它改造成适合自己习惯的样子。1. 项目概述与核心思路1.1 为什么我需要一个“穴居人”级别的工具先说结论我的笔记需求其实只被满足了一小部分。过去我用各种笔记软件大部分时间都浪费在了“整理”而不是“记录”上。Notion 里建了一个数据库想着按标签分类、建关联视图结果记一条会议纪要要打开网页、新建条目、填写属性、选择分类等这些都做完下一件事已经在催我了。Obsidian 好一点但插件体系本身也是个时间黑洞。我真正需要的不是一堆花哨功能而是三个动作打开终端敲一行命令输入内容回车——完事。后来我意识到90% 的笔记场景根本不需要数据库。一条想法、一段摘抄、一个待办、一句灵感本质上就是一段纯文字。如果我不给它套上任何格式和结构的包袱它反而能被更自由地保存和检索。于是caveman的核心思路就定下来了把笔记拆成最原始的单元即一个带时间戳的文件名加一段正文。按天存目录靠内容里的#标签和grep做检索根本不用建索引。这一点设计决策背后的逻辑值得展开聊聊。我见过很多人一上来就用 SQLite 建标签表、做反向索引结果就是工具越写越复杂最后变成了另一个 Notion。对个人笔记工具来说数据量级通常在几千到几万条文本这个规模用grep -r全文搜索实测响应时间在几百毫秒级别完全够用。数据库引入的序列化、迁移、损坏恢复问题反而成了负担。1.2 技术选型的取舍为什么是 Bash 而不是 Python/Rust有人可能会问既然是写命令行工具为什么不用 Python 或 Rust我的考虑有三点。第一零依赖是硬需求。Bash 在任何 Linux、macOS 服务器上都是标配Python 不一定装Rust 更不用说。第二启动速度。Bash 脚本启动只要几毫秒Python 解释器光加载就得几十毫秒如果每次记笔记都要等新鲜感很快就没了。第三可读性和可修改性。我的目标是让caveman像洞穴壁画一样直白——任何人打开脚本十分钟就能看懂全部逻辑改起来也不心疼。这个选型不是没有代价。Bash 处理 JSON、Unicode 排序、复杂数据结构都比较别扭所以我在设计上刻意避开了这些场景不用 JSON 作为存储格式不用数组做复杂关联所有数据都是“按行读、按行写”。代价换来的是极致的部署体验把脚本拷贝到任意一台机器chmod x立刻能用。1.3 明确应用场景它能做什么、不该做什么caveman适合交付以下任务快速捕获开会时、通勤时、写代码摸鱼时冒出来的想法3 秒内记录。书摘与引用把读到的好句子原文存进去顺手加个#书籍标签。任务待办用#todo标记待办项干完就加#done简单粗暴。日志式记录按天归档的流水账比如每天部署记录、实验参数、踩坑备忘。周报素材周末用caveman export导出本周所有笔记闭着眼睛剪一剪就是周报。反过来它不适合做什么不适合存图片、PDF 等二进制附件不适合做多人协作不适合需要复杂表格和多级分类的文档管理。这些需求应该交给正经的文档系统而不是硬塞给一个“穴居人”。工具就该各司其职不越界反而更好用。2. 核心细节与应用场景解析2.1 数据模型设计目录、文件名与内文格式caveman的数据层完全建立在文件系统之上这是它简单到极致的根因。我先定义一下默认结构你可以用环境变量CM_HOME改根路径~/.caveman/ ├── 2026/ │ ├── 2026-03/ │ │ ├── 2026-03-14/ │ │ │ ├── 20260314_153001.md │ │ │ ├── 20260314_153045.md路径规则是年份/月份/日期/时间戳.md。每个文件是一个.md纯文本文件名是YYYYMMDD_HHMMSS.md正文第一行可以写标题其余随意。我不用单独的元数据文件因为文件名本身就携带了最重要的时间信息。标签直接写在正文里我推荐统一用#标签格式后文会讲find命令怎么把它玩出花来。为什么这么设计这里面有一个关键思路文件系统本身就是索引。按年月日分目录肉眼就能直接浏览时间戳保证文件永不重名正文纯文本保证任何编辑器都能打开修改。如果哪天caveman脚本丢了你的笔记也只是散落在目录结构里的普通文本文件随便用什么工具都能找回来。这比任何私有格式都安全。2.2 核心命令设计六条命令覆盖九成场景一个命令行工具命令设计和命名直接决定使用体验。caveman的命令我压到了六条全部可以拼成两个词caveman add 内容快速记录一条笔记自动加上时间戳并存入当天目录。caveman find 关键词在所有笔记里做全文搜索支持正则默认按时间倒序。caveman list [日期]列出某天的所有笔记摘要缺省显示今天。caveman tag 标签名列出包含某个标签的所有笔记。caveman export [日期范围]汇总导出范围内笔记到单个文件。caveman archive [年]把老旧笔记打包成 tar.gz 并移出活动目录。在动手写代码之前我把高频使用场景全列了一遍发现所有操作都可以映射到这三类能力写入、检索、导出合并。很多工具死掉就是因为命令太多、参数太杂用户根本记不住。caveman的哲学是命令超过六条说明设计没想清楚。比如“编辑一条笔记”这个需求我故意不做成命令你直接caveman find 关键词拿到文件路径用vim打开改就是了。2.3 关键使用场景的手把手示例先演示最核心的“三秒记录”。假设我在写代码时突然想到一个优化方案$ caveman add 把渲染模块的缓存策略改为 LRU #todo #性能优化这条命令的执行逻辑是生成时间戳20260314_153001创建目录~/.caveman/2026/2026-03/2026-03-14/把内容写入20260314_153001.md最后打印出文件路径。整个过程我实测过从按键到松开回车不到两秒。找东西的时候$ caveman find LRU输出大概是这样的[2026-03-14 15:30:01] ~/.caveman/2026/2026-03/2026-03-14/20260314_153001.md 把渲染模块的缓存策略改为 LRU #todo #性能优化如果你贴标签caveman tag 性能优化会把所有带#性能优化的笔记列出来按时间倒序。这种标签检索比你想的更灵活因为标签只是正文里的一个词你完全可以在行内任意位置插入不需要维护一个单独的标签表。3. 实操过程与核心环节实现3.1 安装部署把脚本放进 PATH 就够了caveman的安装步骤简单到没什么可写但我还是把它完整交代一遍因为这里面有一个隐藏坑。第一步把脚本保存为caveman文件第二步执行chmod x caveman第三步把它放到 PATH 目录里比如/usr/local/bin/。就这样没了。我建议加一行环境变量到你当前的 shell 配置文件里.bashrc 或 .zshrcexport CM_HOME$HOME/.caveman如果你不加这一行脚本会用默认的~/.caveman。加它的目的不是为了必须而是为了让你将来可以把笔记目录改到任何地方——比如某个网盘同步目录或者/data/notes挂载盘。这里有个我踩过的坑如果脚本里有#!/usr/bin/env bash那第一行 shebang 必须指向真实的 bash 路径。macOS 自带的 bash 是 3.2 版本支持不了shopt -s的一些新参数建议 macOS 用户直接用/opt/homebrew/bin/bash或者干脆用 zsh 跑。后面我会专门讲这个兼容性问题的细节。3.2 核心脚本实现命令分发与 add 命令整个caveman脚本的核心是命令分发逻辑。我用的不是 getoptsBash 的 getopts 不支持长短参数混用懒人场景没必要上而是直接按$1匹配子命令。最关键的部分我摘出来给你看#!/usr/bin/env bash CM_HOME${CM_HOME:-$HOME/.caveman} CM_DATE$(date %Y-%m-%d) CM_STAMP$(date %Y%m%d_%H%M%S) CM_YEAR$(date %Y) CM_MONTH$(date %Y-%m) CM_DIR$CM_HOME/$CM_YEAR/$CM_MONTH/$CM_DATE ensure_dir() { mkdir -p $CM_DIR } cmd_add() { local content$* [[ -z $content ]] { echo 内容不能为空; exit 1; } ensure_dir local file$CM_DIR/${CM_STAMP}.md printf %s\n $content $file echo $file }注意几个细节。第一$*把所有参数合并成一个字符串这样caveman add 多 个 单 词也能正常记录不需要你手加引号。第二ensure_dir用了-p目录存在时不会报错这保证了同一天多条笔记都写到同一个目录里。第三输出返回的是文件绝对路径方便你后续直接vim打开或复制路径。cmd_find 的实现稍微复杂一些因为我做了三件事排除非内容文件、支持正则、统计行号。核心就一行cmd_find() { grep -ri $1 --include*.md -l $CM_HOME | sort -r | head -20 | while read -r f; do local ts; ts$(basename $f .md) echo [$ts] $f grep -i --colornever $1 $f | head -3 | sed s/^/ / done }这里我为什么不用grep -r直接打印匹配行因为会连补全文件路径和上下文一起打出来看起来很乱。我先拿到文件列表再对每个文件只取前三行匹配内容作为上下文预览输出格式既整洁又可控。3.3 检索与标签统计的细节优化如果你有几千篇笔记grep -r $CM_HOME依然快但你可以做得更好。我在cmd_tag中直接匹配“#标签名”而不是普通关键词这样能避免把标签当普通词打乱结果。实现上只比find多了一步cmd_tag() { local tag$1 grep -ri ^\|#${tag}\| \|$ --include*.md -rl $CM_HOME | \ while read -r f; do echo $(basename $f .md) $(head -1 $f) done | sort -r }这里有个小细节grep的-r会读取所有文本文件但我把标签限定为出现在行首、行中空白后、行尾后三种位置防止匹配到“#标签2”这种子串。不过这个正则还有一个隐患如果标签后跟着中文标点“。”匹配会失败。我目前的做法是劝自己在标签后加空格或者避免在中文句子里贴标签。这个取舍我放在后面的常见问题里细讲。3.4 导出与归档把积累变成资产caveman export是我写周报和月度总结时离不开的命令。它的逻辑也很朴素把某个日期范围内的所有笔记按时间顺序合并成一个 Markdown 文件。我设定参数格式是caveman export 2026-03-01 2026-03-31实现如下cmd_export() { local start_date$1 end_date$2 local start_ts end_ts start_ts$(date -d $start_date %Y%m%d_%H%M%S 2/dev/null || date -jf %Y-%m-%d $start_date %Y%m%d_%H%M%S) end_ts$(date -d $end_date %Y%m%d_%H%M%S 2/dev/null || date -jf %Y-%m-%d $end_date %Y%m%d_%H%M%S) find $CM_HOME -type f -name *.md | while read -r f; do local base; base$(basename $f .md) if [[ $base $start_ts $base $end_ts_235959 ]]; then cat $f; echo fi done | sort $CM_HOME/export_${start_date}_${end_date}.md }这里有个跨平台的坑GNU date 和 BSD datemacOS的参数格式不一样。我在脚本里写了两套命令用date -d失败时自动回退到date -jf。如果你只在 Linux 上用可以删掉分支。end_ts加了后缀_235959是为了让当天最后一秒的笔记也能被包含进来这个细节我翻车过一次。archive命令更简单就是把指定年份的目录打包并排除出现频率低的检索范围。我实际上很少手动跑它而是放到 crontab 里每天凌晨执行一次自动把三年前的笔记归档成caveman-2023.tar.gz腾出活跃目录。3.5 自动补全和别名配置让工具顺手起来安装完脚本只算完成了 70%。我强烈建议在你自己的 shell 配置里加两个东西。第一个是别名输入命令越短越容易坚持使用alias ncaveman add alias scaveman find这样记笔记变成了n 值得记录的事情搜索变成了s 关键词几乎不需要改变输入习惯。第二个是补全如果你用 zsh可以在 .zshrc 里加compdef _caveman caveman _caveman() { _values caveman commands add find list tag export archive }补全这东西看起来很细枝末节但实际用起来非常提升幸福感。我见过太多人建了 alias 却不配补全时间一长命令拼写错误频发最终放弃使用。4. 常见问题与排查技巧实录4.1 中文乱码、排序与编码问题Bash 默认的sort用的是字节序对中文 UTF-8 的处理符合字典序吗实测下来符合因为 UTF-8 的字节序和 Unicode 码点是兼容的。不过我踩过另一个坑如果笔记里混入了 GBK 编码的文件比如从 Windows 拷过来的grep会输出乱码甚至造成匹配失败。我的解决办法是在安装脚本时强制统一编码所有新建笔记都用 UTF-8导入旧文件前先执行iconv -f GBK -t UTF-8转换。还有一个小坑sort -r对时间戳文件名排序时因为文件名格式固定为YYYYMMDD_HHMMSS纯字节序正好等于时间顺序所以一切都稳。万一你改了文件名格式排序就会乱建议保持这个命名规则。4.2 grep 搜索变慢的优化方案用事实说话我目前~/.caveman里大约存了两万多个.md文件grep -ri全文搜索“性能优化”实测耗时在 0.3 到 0.6 秒之间完全在可接受范围。如果你发现搜索变慢了原因一般是目录里混进了大量非笔记文件比如导出的 HTML、图片附件grep -r会在二进制文件上反复报错。对策是给所有命令的搜索路径加--include*.md并在归档时把附件单独放一个assets/目录。如果两万篇变成二十万篇该怎么办我目前没有这个量级但有两种无痛抗膨胀方案一是按年归档活跃年份保持在几万篇以内二是换用rgripgrep把脚本里所有grep替换成rg性能能再快一个数量级。我建议直接alias greprg之前先测一下因为rg默认跳过隐藏文件和二进制文件有些行为差异需要适配。4.3 数据安全与多设备同步我从不信任任何“自动云同步”的笔记工具所以caveman的同步方案也走极简路线用git。在~/.caveman目录里执行git init每天或每次修改后提交一次。这样你把仓库推到自己的私有 Git 服务或网盘同步目录就等于获得了版本历史 多端访问。这里有一个我强烈推荐的习惯给 git 提交信息加上日期。我的自动提交命令是0 2 * * * cd ~/.caveman git add -A git commit -m backup $(date \%F) /dev/null 21注意 crontab 里%要转意。如果你有多台设备同步前先git pull --rebase防止覆盖冲突。因为笔记都是文本文件rebase 冲突极少见出现冲突时直接看冲突标记手动合并即可这个代价完全可以接受。4.4 常见问题速查表问题现象原因解决方案caveman add没反应脚本没有执行权限chmod x /usr/local/bin/cavemanmacOS 上报 date 命令错误BSD date 参数不兼容使用脚本中的date -jf回退分支或装 GNU coreutilsgrep 搜索中文无结果文件编码非 UTF-8用file命令检查iconv转换编码搜索很慢活动目录太大或混入附件按年归档添加--include*.md标签匹配到错误内容标签后紧跟中文标点养成“#标签 后加空格”的习惯多设备同步后文件冲突两端同时修改git pull --rebase手动解决冲突笔记文件被误删无备份配置 git 自动提交4.5 我最想分享的三个独家技巧第一给标题留一行。虽然caveman不区分标题和正文但我强烈建议每次记录时把第一行当作标题这样head -1在列表显示时特别清爽。第二在函数名前加cmd_前缀这样grep ^cmd_ caveman就能快速列出所有子命令相当于自带“命令文档”。第三给编辑留快捷方式与其写一个edit命令不如在find结果中直接打印文件路径配合echo $(caveman find xxx | awk {print $2})拿到路径后用$EDITOR打开这种松耦合设计让工具的使用方式更灵活。5. 个人实操经验总结与后续扩展我从写下第一行脚本到现在已经用了大概三个月最大的体会是工具简单到一定程度你反而会愿意多用。以前我在 Notion 里记笔记总想着“结构对不对、标签全不全”现在caveman add一敲内容落盘为安心里毫无负担。这种“生成文本文件”的完成感比“在数据库里建了一条记录”踏实得多。如果这个项目要继续往下做我目前有几个明确的扩展方向。第一个是给检索结果加一个“相关标签云”本质上就是统计grep -oh #[^ ]*的频次十来行代码就能做出来能帮你发现笔记主题的聚类。第二个是生成静态 HTML 导出利用pandoc把每周汇总转成网页方便手机端阅读。第三个是接入启动时自动执行的“快速捕获”脚本比如手机端通过 SSH 远程执行caveman add这样即使不在电脑前也能把想法传回来。不过我不打算急着加这些功能。caveman的核心价值正在于它的“原始”和“克制”。任何新功能如果做不到“三分钟讲清楚、十行代码实现”就说明它破坏了设计哲学。把工具保持在一个能轻松理解全貌的状态本身就是一种对抗复杂度膨胀的方式。最后送给大家一个我在实际使用中最有收获的小习惯每周花十分钟跑一次caveman export把本周记录扫一遍勾掉已完成的补上遗漏的。这个过程不需要任何智能化仅仅是“回顾原始记录”这个动作就已经帮我减少了很多遗忘和焦虑。工具从来不是目的记录和思考才是。caveman只是帮我把这些事变得更轻、更快、更自然罢了。
返回列表