ARTICLE DETAIL

资讯详情

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

Caveman方法论:用Shell脚本与TSV构建可持续十年的极简时间记录系统

Caveman方法论:用Shell脚本与TSV构建可持续十年的极简时间记录系统 Caveman这个词最近在技术社区被反复提起。我第一次看到这个项目名时以为是哪个游戏或恶搞仓库点进去才发现它把穴居人的精神做成了工程方法论——用最简单、最原始、最可控的手段去解决真实存在的问题。围绕caveman能延伸出非常多讨论从个人工作流到极简软件开发再到时间管理和数据记录。这篇文章我会用自己的实操来拆解caveman到底意味着什么以及我是怎么把它落地成一套返祖式的个人时间记录系统。如果你受够了各种“全家桶”工具想用一个能持续用十年、完全属于自己、逻辑一眼看穿的东西这篇值得看下去。1. Caveman背后到底是一种什么思路1.1 从名称到精神为什么要把自己变成穴居人caveman直译是穴居人很多开发者一听就会笑因为它自带一种“原始、粗糙、不讲究”的画面感。但技术圈里用这个词并不是贬义更多是在表达一种刻意收敛的审美不追求功能堆砌不追逐热门框架而是回到问题的本源用最低的技术含量把事情做扎实。我见过无数人把个人知识管理系统、任务管理系统玩成了一场“工具军备竞赛”。一开始用笔记本然后上Notion接着发现数据库不够灵活又切到Obsidian再配一堆插件最后还要折腾同步、主题、工作流。一个月后时间全花在收拾工具上真正该做的事反而没推进。这种体验我相信很多人都有工具越复杂维护成本越高最后连打开它的勇气都没有。Caveman的解法很粗暴——把工具砍到只剩一块石头。想想穴居人需要什么能生火、能储物、能记录最基本的信息。对应到软件上就是纯文本、极简脚本、一张表。没有花哨的UI没有云端强绑定没有不可见的魔法。所有的设计都摆在明面上出了问题一眼能找到根源。我发现这套思路特别适合记录类的事时间追踪、日志、想法速记。这类场景的共同特点是——高频、轻量、不需要复杂查询但最怕“记录动作本身太重”。如果每次记录都要打开App、点几次按钮、填几项表单人很快就会放弃记录。而caveman风格的工具把记录动作压缩到“敲两个字符回车”这才是它能长期坚持下来的根本原因。1.2 为什么“笨办法”在复杂时代依然能打很多人会质疑现在AI都能自动生成周报、自动识别会议纪了为什么还要回到原始方式我的看法是笨办法的价值不在“功能弱”而在“可控性极强”。举个栗子。杀毒软件和防火墙做了那么多层防护但很多资深工程师排查网络问题时的第一步仍然是ping、traceroute这种几十年前就存在的命令。同理你给一个团队上Jira、Confluence权限配一堆字段建一堆最后大家照样私下拉微信群对需求。工具并没有让协作变透明反而让真实状态被隐藏在了表单后面。caveman风格的思路是先把最核心的那条链路打通其他一切都可以延后。它符合一个工程原则可维护性优先于功能性。一个能稳定用十年的简陋系统远远好过一个功能华丽但经常崩溃、需要不断升级维护的复杂系统。这一点放到个人工具上更加显著因为个人工具的维护者就是你一个人你不可能像企业一样养一个团队来维护它。所以每引入一个新特性之前都应该问自己这个功能我在未来三年里真的会频繁使用吗如果不是就不要加。Caveman还特别适合用来做“数据所有权”的示范。你不依赖任何商业产品不担心厂商跑路不担心格式锁定。数据就是几个文本文件放在自己的硬盘里想去哪就去哪。我甚至用过最原始的方案把一个文件夹挂到云盘手机上直接用文本编辑器打开改电脑端也读取同一份文件。整个过程没有API、没有认证、没有套餐这就是caveman的浪漫。2. 搭一套极简时间追踪系统的完整实操2.1 目录结构与设计原则先交代背景我平时有大量自由安排的时间写代码、写文章、读书、开会、做家务、健身。如果没有记录一周结束根本想不起来时间都花哪了。所以我需要一套时间追踪系统要求是打开终端三秒内完成一次记录数据格式肉眼可读统计可以交给脚本处理。整个系统我放在~/cave/目录下结构如下~/cave/ ├── bin/ │ ├── log.sh # 核心计时脚本 │ ├── memo.sh # 快速备忘脚本 │ ├── query.sh # 日常查询脚本 │ └── weekly.sh # 周报生成脚本 ├── journal/ │ ├── tsv/ # 按月存放原始记录 │ │ ├── 2026-01.tsv │ │ └── 2026-02.tsv │ └── memo.md # 备忘流水 ├── data/ │ └── cave.db # SQLite查询库 └── state/ └── current # 记录当前正在进行的任务设计原则很简单TSV文件是唯一的事实来源一切脚本生成的数据库都可以随时丢弃重建。SQLite只是查询用的衍生品删了也不影响原始数据。这样就把“记录”和“分析”彻底解耦了。为什么用TSV而不是CSV因为CSV要考虑逗号转义TSV用制表符分隔而项目名、时间字段里极少出现制表符处理起来干净利落。更关键的是TSV按行追加、按行读取用 shell 自带的cut就能操作不需要任何编程语言参与。为什么不用JSONJSON写起来麻烦还要处理括号和引号对“瞬间完成记录”这个目标来说是种负担。2.2 核心计时脚本log.sh的完整实现我的核心诉求是类似秒表一样的使用方式开始一个任务记录任务名结束一个任务自动算出时长。脚本逻辑其实非常简单完整版本贴出来供直接使用#!/usr/bin/env bash # ~/cave/bin/log.sh set -u CAVE$HOME/cave TSV$CAVE/journal/tsv/$(date %Y-%m).tsv STATE$CAVE/state/current now() { date %Y-%m-%d %H:%M; } ACTION${1:-} PROJECT${2:-} case $ACTION in start) if [[ -z $PROJECT ]]; then echo 用法: log.sh start 项目名 exit 1 fi # 如果上一次任务忘了结束自动补上结束时间 if [[ -f $STATE ]]; then read -r OLD_START OLD_PROJECT $STATE echo -e ${OLD_START}\t$(now)\t${OLD_PROJECT} $TSV rm -f $STATE echo 自动关闭上次未结束任务: $OLD_PROJECT fi echo -e $(now)\t${PROJECT} $STATE echo 开始记录: $PROJECT ;; stop) if [[ -f $STATE ]]; then read -r OLD_START OLD_PROJECT $STATE echo -e ${OLD_START}\t$(now)\t${OLD_PROJECT} $TSV rm -f $STATE echo 已记录: $OLD_PROJECT ($OLD_START → $(now)) else echo 当前没有正在记录的任务 fi ;; *) echo 用法: log.sh start|stop 项目名 exit 1 ;; esac用的时候~/cave/bin/log.sh start coding ~/cave/bin/log.sh stop我给这两个命令起了别名alias lg~/cave/bin/log.sh之后的工作流就变成写代码之前敲lg start coding写累了或者切去开会敲lg stop。整个记录过程不到两秒甚至比掏出手机开App还快。这里有个细节值得多说一句start命令自动关闭上一次未结束的任务。这个设计是针对“忘记stop”这种事后的补救十分关键。比如你晚上十点开始看论文看了十分钟就去刷手机了凌晨两点突然想起来还没记录这时候不需要补录之前的十分钟只需要执行一个新的start命令它会自动把上一段以当前时间作为结束点写进TSV。数据虽然不够精确但至少没有丢失。2.3 备忘与查询memo.sh和query.sh时间记录只能告诉你“时间花在哪”不能告诉你“当时在想什么”。所以我额外加了一个轻量备忘机制这就是memo.sh#!/usr/bin/env bash # ~/cave/bin/memo.sh CAVE$HOME/cave echo -e $(date %Y-%m-%d %H:%M)\t${*} $CAVE/journal/memo.md用法~/cave/bin/memo.sh 完成了caveman初稿的大纲这样所有零散的想法、灵感、会议结论都能迅速归档。memo格式同样是TSV一行一条永不修改只有追加。这正是caveman的核心不需要编辑、不需要分类、不需要标签只要保证“记录下来”这个动作发生了。查询端我用一个query.sh来快速浏览今天的记录#!/usr/bin/env bash # ~/cave/bin/query.sh CAVE$HOME/cave TODAY$(date %Y-%m-%d) grep -P ^${TODAY} $CAVE/journal/tsv/*.tsv输出类似2026-07-15 09:00 2026-07-15 11:35 coding 2026-07-15 11:36 2026-07-15 12:20 reading一眼扫过去就知道今天干了什么。这里有个小技巧TSV里的时间格式是YYYY-MM-DD HH:MM这个格式在字符串排序上就是时间排序grep、sort、cut都可以直接处理不需要转换成时间戳。这也是我踩过不少坑之后发现的数据格式越接近人眼可读的标准时间越容易处理。不要为了省几个字节用1721019600这种Unix时间戳到最后你自己都看不懂。3. 把原始数据变成有效洞察3.1 SQLite导入与基础统计TSV文件可以人工阅读但要做汇总统计就不方便了。这时候才轮到SQLite出场。我不推荐直接用Excel打开TSV因为你每次还得手动导入几秒钟的功夫积累起来也是负担。更好的做法是写一个导入脚本把TSV的增量数据同步到SQLite。导入脚本逻辑#!/usr/bin/env bash # ~/cave/bin/import.sh DB$HOME/cave/data/cave.db TSV_DIR$HOME/cave/journal/tsv # 全量重建查询表保证重复导入不会产生脏数据 sqlite3 $DB EOF CREATE TABLE IF NOT EXISTS timeline( start TEXT, end TEXT, project TEXT ); DELETE FROM timeline; .mode tabs EOF for f in $TSV_DIR/*.tsv; do echo 导入 $f ... sqlite3 $DB .mode tabs .import $f timeline done echo 完成因为timeline完全是TSV的投影所以定期重建也没有心理负担。真正宝贵的是原始TSV数据库随时可以从零再生成。导入后能做很多统计。比如按项目汇总本月时长SELECT project, ROUND(SUM((julianday(end) - julianday(start)) * 24), 2) AS hours FROM timeline WHERE start 2026-07-01 AND start 2026-08-01 GROUP BY project ORDER BY hours DESC;julianday()是SQLite自带的函数可以把日期字符串转成天数相减再乘24就得到小时数。这个查询会输出类似coding 42.50 reading 15.25 meeting 6.75看到这些数字的时候你会有一种“原来我的时间是这样流走的”的实感。光这一点就足够支撑你每天花两秒去记录。3.2 周报、热力图与周期回顾除了基础统计我还给自己做了一个“极简周报”每周五下班前自动汇总最近七天每个项目的累计时长。脚本同样简单#!/usr/bin/env bash # ~/cave/bin/weekly.sh DB$HOME/cave/data/cave.db sqlite3 -header -column $DB SELECT project, ROUND(SUM((julianday(end) - julianday(start)) * 24), 2) AS hours FROM timeline WHERE start datetime(now, localtime, -7 days) GROUP BY project ORDER BY hours DESC; 从趋势上看我会特别留意两类异常一类是某个项目时间从上周的20小时暴跌到本周的2小时另一类是总时长明显超过55小时。前者通常意味着任务被意外搁置后者意味着睡眠或休息被牺牲了。数据本身不会给出结论但它会提示你“这块需要看一眼”。我也试过做热力图比如用 Python 的 matplotlib 画全年每日投入时长。但后来发现真正常看的只有两个视图今天的明细、本周的汇总。太花哨的图让我这个“穴居人”头疼而且生成过程本身又引入了额外的依赖。caveman的精神是砍掉不必须的东西所以我的热力图最后退化成一个终端字符条sqlite3 $DB SELECT substr(##################, 1, ROUND(SUM((julianday(end)-julianday(start))*24))) FROM timeline WHERE start date(now, localtime, -14 days) GROUP BY date(start); 每个#代表大约一小时一眼看过去哪几天废了、哪几天高产清清楚楚。3.3 用脚本生成Markdown周报光在终端看还不够我每周会生成一份Markdown周报发到自己的笔记里存档。生成脚本是这样写的#!/usr/bin/env bash # ~/cave/bin/report.sh DB$HOME/cave/data/cave.db OUT$HOME/cave/reports/week_$(date %Y-%m-%d).md { echo # 本周时间周报 $(date %Y-%m-%d) echo echo ## 项目时长汇总 echo sqlite3 -header -markdown $DB SELECT project, ROUND(SUM((julianday(end) - julianday(start)) * 24), 2) AS hours FROM timeline WHERE start datetime(now, localtime, -7 days) GROUP BY project ORDER BY hours DESC; echo echo ## 本周备忘 echo tail -n 20 $HOME/cave/journal/memo.md } $OUT echo 已生成: $OUTSQLite的-markdown选项会直接输出带表格语法的Markdown省去手拼字符串的麻烦。配合crontab每周五下午6点自动跑一次我的邮箱里就会安静地躺着一份总结。整个周报没有使用任何在线工具生成逻辑不到30行任何人打开这个脚本都能在两分钟内搞懂它做了什么这才是工具应该有的样子。4. 实际使用中踩过的坑和解决办法4.1 忘记停止计时导致的数据失真任何时间追踪工具最怕的就是人忘记操作。我用了这套系统之后最常见的场景是早上开始写代码中午被叫去吃饭饭局回来已经下午三点想起来还没stop。这时候如果直接stop记录会变成“06:00 到 15:00 在写代码”9个小时显然失真。我的解决方案分三层。第一层是脚本自动修复每次执行新的start时如果发现state/current里有未完成的任务就自动以当前时间当作end把它追加进TSV至少保证数据不丢。第二层是事后校正我会在TSV文件里手动把那段过长的记录删掉再单独用cat手工追加一条接近真实情况的记录。TSV本身就是文本用编辑器改就能修复这种自由度是商业软件给不了的。第三层是恕错长时间没动电脑的任务时长本来就没法精确测量接受30分钟以内的误差就好。别为了十分钟误差去折腾automatic tracking那又落回“工具束缚人”的圈套了。4.2 项目命名混乱与统计分叉刚开始用的时候项目名完全靠随手敲。结果一周后统计时发现光“写文章”就有四种叫法writing、blog、article、post。对于SQLite的GROUP BY来说它们是完全不同的项目统计结果被切成了碎片。后来我在log.sh里加了一个项目白名单启动的时候检查项目名是否在~/cave/projects.list这个文件里如果不在就提示你是“新建项目”还是“归入已有项目”。这样一来项目名就被圈定在一个可控集合里统计分叉的问题基本绝迹。# 在log.sh的start分支加入 LIST$CAVE/projects.list if ! grep -qx $PROJECT $LIST 2/dev/null; then echo 项目 [$PROJECT] 不在白名单中 select CHOICE in 新建项目 继续使用; do case $CHOICE in 新建项目) echo $PROJECT $LIST; break;; 继续使用) break;; esac done fi这个限制看起来反人性实际上却是防止混乱的好办法。越自由的系统越容易失控适度约束反而能提高数据的长期可用性。4.3 数据同步、备份与多设备录入caveman的数据全在本地一旦硬盘坏了就全没了。所以我必须把~/cave/目录纳入同步。我的做法是把整个目录放到一个用Git管理的文件夹里每次完成若干记录后顺手git add . git commit同时在远端仓库留一份副本。其实云端同步也试过比如把目录丢进网盘同步文件夹。刚开始没觉得有问题后来SQLite数据库出现“database is locked”因为网盘同步进程和本地进程同时对数据库文件进行操作。排查半天才发现是WAL日志文件在捣鬼。最终我把SQLite的journal模式限制成DELETE并且只在查询时打开数据库平时不让它常驻即便如此网盘进程偶尔还是会给几个文件加锁。最后我干脆放弃了让SQLite参与同步同步直接针对TSV和memo.md本体数据库需要查询时随时重建。这种方案最稳定也不容易出幺蛾子。多设备录入的问题没有完美解决。我目前的方案是手机端的随手记录靠备忘录回到电脑上统一补录进memo.md。补录操作非常轻一天一般也就几条。如果哪天需要在场外精确计时我会用Termux在手机上跑同一条log.sh把整个~/cave仓库克隆到手机录完push回远端。注意手机上的录完回到电脑上先 pull 再继续用不然两边会各自在本地生成不一致的TSVMerge起来很痛苦。5. 让这套系统继续扩展的玩法5.1 自动提醒与每日回顾记录只是第一步持续性的回顾才是价值所在。我写了一个daily.sh在每天21:30通过crontab自动运行它把今天的timeline按时间顺序打印出来再附上memo的最后若干条记录。这样每晚只要扫一眼就能复盘今天的时间流向。# 每天21:30运行提醒并汇总当天记录 30 21 * * * $HOME/cave/bin/daily.sh $HOME/cave/logs/cron.log 21有人会问crontab和脚本不也是额外的工具依赖吗没错但它们一次配置终身受用不像那些每年都要手动升级的网页应用。自动化在这里的意义不是“增加功能”而是“解放记忆”——你不需要记得自己几点做了什么因为脚本会替你问。5.2 周报自动生成与轻量分享上一节已经把report.sh的生成逻辑写出来了配合crontab每周执行它会在每周五晚上自动生成Markdown周报。生成的周报除了存档我还会把它在IM软件里共享给自己和合作者——这一步通常就是一行curl调webhook的事不值得为它专门搭一个平台。如果团队成员需要参与同一套时间记录我也试过把TSV文件放到一个共享只读的Git仓库里大家各自在本地记录每周合并一次汇总。坦白讲这种方式对信任要求很高不太适合大型团队但用在两三个人的独立小团队里效率反而比上Jira高得多。因为每个人都清楚自己写下的每一行是什么而不是被一堆自动生成的燃尽图淹没。5.3 标签统计与年度复盘记录的粒度可以继续细化。我后来给项目名增加了标签支持比如writing:caveman表示“写caveman相关文章的创作时间”。统计时用SQL的字符串函数截取标签部分就可以横向对比同一类别在不同项目间的投入。SQLite支持substr、instr这条查询写起来并不难SELECT substr(project, instr(project, :) 1) AS tag, ROUND(SUM((julianday(end) - julianday(start)) * 24), 2) AS hours FROM timeline WHERE project LIKE %:% GROUP BY tag ORDER BY hours DESC;到了年末我会把整年数据拉出来做一次简单回归哪些项目占据了我大量时间它们和今年的核心目标是否一致这个环节不需要复杂算法几个GROUP BY就够。比起那些动不动就生成几十页报告的时间管理App我更信任自己手里的这一张TSV表因为它没有任何隐藏的统计偏差一切计算逻辑都在我的掌控中。我实际用下来的体会这套caveman风格的工具我差不多用了三年。头一个月会不适应觉得太简陋坚持下来之后我反而对复杂工具越来越警惕。每次想给系统加新功能我就会先问这个功能能让我更快开始记录吗如果答案是否定的就砍掉。时间记录这件事真正难的不是可视化、不是数据分析而是“每天坚持记录”本身。让记录动作小到无感比任何漂亮的图表都有用。一个小技巧送给想尝试的人除了alias lg你还可以把记录命令挂在终端快捷键上或者写一个t函数来读到当前shell里最近一次stop的项目名方便快速切换任务。我自己常用的一条是给log.sh加了个switch参数先stop当前任务再start新任务一次搞定任务切换不用敲两遍别名。如果你也想试试这种返祖式的时间记录不用抄我全部代码先建个目录放一个log.sh坚持一周你大概就会理解caveman的魅力在哪里。工具是给人用的别把人变成工具的奴隶。
返回列表