资讯详情

从“工具负担”到“穴居人”:用Shell和Python写极简命令行记录工具

📅 2026/10/29 13:21:21 | 华诺云谱 👁 阅读
从“工具负担”到“穴居人”:用Shell和Python写极简命令行记录工具
如果你手头的项目多到一定数量大概率会产生一种感觉真正消耗精力的不是干活本身而是维护那些号称帮你干活的工具。最近几个月我一直在用自己写的一个小工具名字就叫caveman直译过来是穴居人。它不是什么大型项目也做不出啥花哨效果唯一擅长的事情就是用最原始、最直接的方式帮我完成记录、归档和查询。我把这套东西发在技术社区后陆续有朋友来问设计思路和踩坑经历这次索性把完整过程写出来从为什么做、怎么设计到实际跑起来有哪些问题一次性讲清楚。caveman 适合两类人一类是手头工具太多、被各种效率神器绑架的开发者另一类是刚接触命令行、想从零写一个顺手小工具的新手。前者能从中看到做减法的价值后者可以直接抄作业把里面的脚本改成自己需要的样子。我不想把它包装成什么框架或平台它就是一堆不超过几十行的 Shell 和 Python 脚本却已经稳定陪我跑了三个月哪怕临时换台电脑五分钟内就能把环境还原出来。1. 这个项目到底是干嘛的给工具负担做减法1.1 问题从哪来我日常的东西很散工作上有待办、有项目笔记、有随手记下的灵感生活里有采购清单、有健身记录、有读书摘抄。最早我用的方案是各种 App 组合备忘录一个、待办事项一个、项目管理一个再加上云笔记。听起来挺齐全但用一段时间就发现问题每个工具都有自己的账号体系、同步逻辑、数据格式我想把一个想法从备忘录挪到云笔记还得先复制粘贴、再调整排版。更难受的是工具越多越不想打开最后整个记录体系基本处于半废弃状态。后来我试着把这堆工具砍到只剩一个结果那个全家桶软件又太重了启动慢、界面堆满了我根本用不到的按钮。我需要的只是一个能存、能查、不丢的地方可这些工具给我的是一堆广告推送和付费套餐提醒。回头看最讽刺的是为了追求效率我引入了一堆复杂工具它们反而成了新的负担。1.2 caveman 的定位与目标caveman 的定位非常窄就是一个命令行下的个人记录工具。它不管日历、不管邮件、不管文件同步只管三件事记录、查询、统计。记录把一条笔记、一条待办、一条日志以最快的速度写进本地文件。查询用关键词、日期、类型去过滤之前记录的内容。统计快速知道这个月写了多少条、连续打卡多少天、某个项目下有哪些条目。这三点听起来简单但真正限制自己只做这三件事才能避免重蹈工具膨胀的覆辙。我给自己定了一条规矩凡是超过五十行的功能就先不做等真需要时再说。三个月用下来需求依然够用说明最初对最小可用的判断没有跑偏。1.3 为什么叫这个名字取名caveman是一次自嘲也是有意的提醒。穴居人给人的印象就是原始、简单、直接生活里没有多余的东西但该有的本事一点不少。我在设计这个工具时就想让它保持这种穴居人气质——不追求花哨不追求功能数量只做少数几件事但做扎实。对比现在的主流工具穴居人反而是个稀缺品质。我用表格列一下自己的感受对比点caveman主流效率工具界面命令行几乎没有界面图形界面功能入口多数据存储本地明文文件云服务私有格式启动速度毫秒级秒级起步频繁更新配置方式一个文本文件层层设置菜单离线能力完全离线多数依赖网络可定制性直接改脚本受限于官方设计当然这不是说图形化工具一无是处。团队协作、富文本排版、多端实时同步这些确实是商业工具的长处。但如果只是个人记录用穴居人方案会轻松得多。数据在自己手里格式完全透明哪怕将来这个工具不维护了文件还在还能用其他脚本解析这才是真正的长期主义。2. 整体设计与技术选型刻意落后反而省事2.1 设计原则三个只动手之前我先给自己立了三条设计原则所有功能都必须过这三道关只用一个入口所有操作都通过caveman这个命令完成不搞多个子程序。只存本地文件不引入数据库不做远程同步数据就是几个普通文件。只读必要的内容启动时不做任何初始化检查不预加载数据执行完立刻退出。这三条原则看着简单实际回过头看非常关键。单一入口保证了心智负担最低我不用记十几个命令本地文件保证了可迁移性备份就是复制文件夹延迟读取保证了启动速度基本零等待。我见过不少个人项目一开始也打着极简旗号后来为了加数据库上了 Redis为了做看板上了一套前端框架。最后变成一个小型 ERP。caveman 的这三条原则就是专门用来防止这种事发生的。2.2 为什么选 Shell 加 Python选技术栈时我认真想过一轮。候选有三个纯 Shell、Shell 加 Python、Go 或 Rust 编译成二进制。纯 Shell 的好处是几乎任何系统都有坏处是处理字符串和日期时很痛苦尤其跨平台兼容性容易出问题。Go 或 Rust 的好处是可以发布单一二进制文件但每次改功能都要重新编译对一个频繁调整的小工具来说拖慢节奏。最后落在 Shell 加 Python 的混合方案上命令入口、参数分发用 Shell因为它天然就是胶水适合把用户输入转发给后端处理。数据解析、日期计算、统计输出用 Python因为处理这些逻辑更安全代码也更清晰。实际用下来这个组合非常舒服。Shell 部分只有几十行主要负责参数校验和调用 PythonPython 部分按命令拆成几个小函数没有复杂的类继承纯粹面向过程写基本谁都能看懂。我在 macOS 和 Debian 上都跑得挺好没有遇到跨平台的大坑。如果非要说一个小缺点就是依赖 Python 环境。好在现在 Linux 和 macOS 基本都自带 Python 3Windows 上稍麻烦但我的场景主要在 Mac 和 Linux 服务器上所以完全接受。2.3 数据存哪纯文本还是 JSON这是设计里最纠结的一点。最初我用的是 JSON 文件每条记录一个对象包含类型、时间、内容、标签。好处是解析方便Python 里json.load就能读。但有个问题直接用文本编辑器查看或修改不太友好而且万一文件里混入非法内容整个 JSON 都会解析失败。后来我做了折中主存储用带行号标记的纯文本每一行是一条记录开头是类型、时间、标签后面是正文。举个例子[note] [2025-01-12 12:30] [灵感] 需求可以反过来想先考虑什么时候不该加功能 [todo] [2025-01-12 12:31] [项目A] 梳理接口文档 [log] [2025-01-12 12:32] [工作] 完成了版本检查脚本这样做的优势非常明显文件可以直接打开看格式一眼就懂就算某个功能坏掉也不影响已有数据用grep就能做临时查询根本不需要专门工具。代价就是 Python 解析时要多写一点正则不过这点成本完全值得。我甚至可以在没有安装 Python 的机器上用cat和grep直接查看内容这种退路让人心里踏实。2.4 整个目录结构长什么样caveman 的代码和全部数据放在同一个目录里方便整体备份。目录结构如下~/.caveman/ ├── caveman.sh # 命令入口脚本 ├── cm_cli.py # Python 核心逻辑 ├── cm_config.ini # 配置文件 ├── data/ │ ├── notes.txt # 笔记记录 │ ├── todos.txt # 待办记录 │ ├── logs.txt # 日志记录 │ └── stats.db # 统计缓存可选总共就这么几个文件。没有node_modules没有虚拟环境没有构建产物。我从一台新机器恢复使用只需要把这个目录拷过去再把caveman.sh软链到PATH里的某个位置就行前后不到五分钟。这种整个家当一拷贝就能走的感觉是很多大型项目给不了的。3. 核心实现解析把穴居人风格落到代码里3.1 命令入口与参数解析入口脚本caveman.sh最核心的工作是判断用户想干嘛然后把参数转给 Python。我用的是最原始的case语法清晰直接不依赖任何额外工具。#!/usr/bin/env bash # caveman.sh - 极简记录工具入口 set -euo pipefail BASE_DIR$HOME/.caveman PY_SCRIPT$BASE_DIR/cm_cli.py CMD${1:-} shift || true case $CMD in note) python3 $PY_SCRIPT note $ ;; todo) python3 $PY_SCRIPT todo $ ;; log) python3 $PY_SCRIPT log $ ;; list) python3 $PY_SCRIPT list $ ;; stats) python3 $PY_SCRIPT stats $ ;; config) python3 $PY_SCRIPT config $ ;; help|--help|-h) echo 用法: caveman [note|todo|log|list|stats|config] ;; *) echo 未知命令: $CMD使用 caveman help 查看用法 2 exit 1 ;; esac这段脚本的关键点有两个。第一set -euo pipefail打开这样脚本遇到未定义变量或管道错误时不会闷声失败因为记录工具宁可报错也不能悄悄写坏数据。第二参数转发用$而不是$*确保带空格的内容能完整传给 Python我第一次写的时候在这吃过亏。3.2 核心命令快速写入Python 侧的逻辑不玩花活我把每个子命令拆成函数按参数个数和数据格式处理。以note为例最简版本的写入函数如下import sys, os, json from datetime import datetime BASE_DIR os.path.expanduser(~/.caveman) DATA_DIR os.path.join(BASE_DIR, data) def ensure_config(): if not os.path.exists(DATA_DIR): os.makedirs(DATA_DIR, exist_okTrue) def write_record(cmd, args, tagNone): ensure_config() path os.path.join(DATA_DIR, f{cmd}s.txt) now datetime.now().strftime(%Y-%m-%d %H:%M) tag tag or 默认 content .join(args).strip() if not content: print(内容不能为空) sys.exit(1) with open(path, a, encodingutf-8) as f: f.write(f[{cmd}] [{now}] [{tag}] {content}\n) print(f已记录到 {path}) if sys.argv[1] note: # 用法: caveman note [-t 标签] 内容... if sys.argv[2] -t: write_record(note, sys.argv[4:], sys.argv[3]) else: write_record(note, sys.argv[2:])有朋友问为什么不用print(写入成功)这样更友好的反馈而是返回文件路径。我的想法是极简工具的每一次输出都要有信息量路径比成功两个字有用得多万一将来想手动打开文件路径可以直接复制。另外追加模式用a而不是w这个看似基础但真的很关键写错一次就会把之前的记录清空。好在我只在测试环境踩过这个坑。3.3 查询与统计怎么处理查询是数据积累之后最重要的能力。我设计的list命令支持按日期范围、按关键词、按类型过滤。实现思路很简单读文件拆行用startswith和in做匹配。没有用数据库索引因为这些文本最多也就几千行每次全量扫描的时间几乎可以忽略不计。def list_records(cmd, keywordNone, dateNone): path os.path.join(DATA_DIR, f{cmd}s.txt) if not os.path.exists(path): print(还没有任何记录) return with open(path, r, encodingutf-8) as f: for line in f: if keyword and keyword not in line: continue if date and date not in line: continue print(line.rstrip())统计这块稍微做了点聪明事。我维护了一个stats.db其实也不是数据库而是一个 JSON 缓存记录每天的写入次数。统计时先读当天缓存再做简单聚合。为什么不每次现场扫文件因为文件会越积越多而大多数统计场景只关心最近三十天缓存能省掉不必要的扫描同时保持逻辑简单。3.4 极简配置一个 INI 文件搞定配置方面我刻意做得很保守。没有命令行配置文件生成器默认配置写在cm_config.ini里内容如下[default] data_dir ~/.caveman/data default_tag 默认 time_format %Y-%m-%d %H:%M history_days 30Python 里用configparser读取读取失败就用内置的默认值。这样设计的好处是配置文件丢了工具还能跑想改默认值直接编辑这个文件就行。我不做复杂的多环境配置因为个人工具的使用场景就是一台电脑、一个用户、一份配置多环境需求往往是自己给自己加戏。4. 完整实操从一台干净机器把它跑起来4.1 环境准备实操前需要准备的非常少一台安装了 Python 3 的 Linux 或 macOS 机器一个能用的文本编辑器。我用的是 Debian 服务器和 MacBook 两个环境实测一致。我建议把整个目录放在~/.caveman/这样既不会污染项目文件夹也方便统一备份。如果对路径有洁癖也可以放在自定义目录但必须在脚本里同步修改BASE_DIR否则运行时会找不到数据。4.2 安装与初始化安装其实就是三步拷贝脚本、创建数据目录、建立软链。mkdir -p ~/.caveman/data cp caveman.sh cm_cli.py cm_config.ini ~/.caveman/ chmod x ~/.caveman/caveman.sh ln -s ~/.caveman/caveman.sh /usr/local/bin/caveman执行完后在任意目录输入caveman help应该能看到用法说明。这里有个很多人第一次踩的坑/usr/local/bin可能不在当前用户的PATH里或者没有写权限。如果command not found可以用echo $PATH检查或者直接用sudo ln -s。我自己更喜欢用~/bin目录然后把~/bin加进PATH末尾这样不用动系统目录。4.3 日常使用演示初始化完成后我模拟一次真实的记录场景# 记一条笔记带标签 caveman note -t 灵感 把某些重复工作脚本化的关键是要先约3分钟流程 # 记录待办 caveman todo -t 项目A 完成caveman博客文章初稿 # 记录日志 caveman log 上午整理了模块接口设计下午写了测试用例每执行完一条终端都会返回数据文件路径方便我随时去查看原始文件。查询时可以用caveman list note # 查看所有笔记 caveman list todo -k 博客 # 只看提到博客的待办 caveman list log -d 2025-01-12 # 只看某一天的日志 caveman stats # 查看最近统计我有一个使用习惯每次记完待办完成后不会删除而是追加一条[x]标记。这样保持历史完整统计完成率时也有据可查。删除记录这件事我本来就没做因为文件是纯文本真想删除直接打开文件编辑反而比数据库操作更直白。4.4 与系统级工具联动caveman 的另一个舒服之处是能和系统自带的工具天然配合。比如每天下班前我通过 cron 或 systemd timer 让它自动生成当天的工作日志占位符# 每天 18:00 自动插入当天日志占位符 0 18 * * * /usr/local/bin/caveman log -t 工作 自动占位请在复盘后补充另外我给caveman note做了个 shell alias让输入更快alias cmncaveman note -t 速记现在遇到临时想法直接敲cmn 这是个值得验证的思路三秒钟就完成记录。这种随手记录的顺畅感是我用过的花哨工具中很少有的。5. 常见问题与排查实录5.1 命令找不到与环境变量问题很多人第一次跑就卡在caveman: command not found。排查顺序我用表格整理出来省得每次翻文档症状可能原因排查方式command not found软链未建立或路径不在 PATH执行which caveman或type cavemanPermission denied脚本没有执行权限执行ls -l ~/.caveman/caveman.sh补chmod x在某些终端能用某些不能PATH 配置不跨 shell检查~/.bashrc、~/.zshrc里的 PATH 配置软链状态是坏的原文件路径被移动过执行ls -l /usr/local/bin/caveman看链接指向我自己的情况是 Mac 上默认 shell 是 zsh但早期只在.bashrc里加了 PATH导致新开终端时命令时有时无。后来统一在.zprofile里配置所有终端会话都正常了。5.2 中文内容乱码如果数据文件里存了中文在终端查询时出现乱码大概率不是工具的问题而是终端编码环境没设对。处理方式export LANGzh_CN.UTF-8 export LC_ALLzh_CN.UTF-8Python 侧读写文件时我已经强制指定encodingutf-8所以只要终端环境变量正确中文显示就不会有问题。如果之前已经写入了乱码文件可以用 iconv 做一次转码补救但如果原始内容已经损坏基本救不回来。这也提醒我一个重要原则记录工具宁可保存速度慢一点也要保证写入的数据是完整的所以我在代码里避免使用快速但可能断行的拼接方式。5.3 文件损坏与备份纯文本文件看起来简单但也有自己的风险。最典型的是如果你同时开了两个终端窗口在同一秒各自执行一次追加写理论上可能发生相互覆盖。虽然概率极低我在代码里也不做文件锁这种情况下最常见的表现是写完的文件行尾异常。我的方案不是锁文件而是做多重备份。每天凌晨用 cron 自动把~/.caveman/data打包压缩到backup/目录0 3 * * * tar czf ~/.caveman/backup/caveman_$(date \%F).tar.gz -C ~/.caveman data保留最近三十天的备份其余清掉。这样即使某个文件坏了也能找到昨日版本。毕竟记录工具最大的价值是长期积累的数据不是代码。5.4 多端同步的冲突问题说到同步我必须提醒caveman 这种单机纯文本的方案是不适合多端实时同步的。我试过把它放在网盘目录里同步结果两台电脑同时编辑时文件冲突版本会让人崩溃。后来我调整了用法以一台主力设备为主其他设备只读。真正要跨设备查看时用网盘的历史版本恢复即可。如果你确实有几台设备轮流用我给两个建议一是把每天的记录任务集中在主力机上二是同步前手动更新一下文件时间戳。不过说实话个人记录里 95% 的场景用不到多端同步为了这 5% 的需求引入同步冲突反而破坏极简属性。5.5 极简陷阱过度简化会变成另一种负担最后想说一个不太被人注意的问题极简本身也可能被过度形式化。有一次我把命令参数改成尽量短比如caveman n代替caveman note结果一周后我自己都忘了n代表什么。那时我突然意识到极简的边界应该是降低认知成本而不是字面意义的短。现在的命令已经固定为完整的note / todo / log / list / stats输入多几个字母无所谓重要的是看一眼就知道在干嘛。6. 后续扩展与个人心得6.1 把 caveman 变成项目周报生成器用了一段时间后我发现log命令里的内容可以二次利用。我写了个小的 Python 片段每周一把logs.txt里本周的记录按日期汇总自动生成周报初稿。实现也不复杂过滤时间戳在最近七天的记录按天分组输出成简单的 Markdown 列表。这让我彻底摆脱了每周回忆上周干了什么的窘境。6.2 加密与远端备份数据是明文放在电脑里一般没问题但如果涉及到敏感信息我建议在备份层面处理而不是改 caveman 本体。我是这样做的用 gpg 对备份包做对称加密然后把加密文件上传到自带的对象存储空间。恢复时先解密再解压。整个过程只有两条命令不在工具的代码里增加任何复杂性。6.3 我在实际使用中的最大体会如果让我说这段折腾里最有价值的部分倒不是脚本本身而是那个做减法的决策过程。每想加一个功能我都会先问自己不加它会有什么实际损失大多数时候答案是没有。caveman 三个月没增加任何新命令我却越用越顺手因为所有注意力都集中在数据内容上而不是工具功能上。最后再分享一个小技巧我会定期打开notes.txt用编辑器做一次人工整理把过时的想法删掉、把有价值的内容合并。这个过程看起来原始却比任何自动整理工具都有效。因为自动整理只能按照你设定的规则移动文字而人工整理能真正帮你重新思考那些记录的价值。这就是我觉得穴居人风格最迷人的地方不依赖复杂的自动化把控制权牢牢握在自己手里。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑