機與安全實踐)
做了十幾年開發(fā)我電腦里最離不開的東西不是什么花哨的圖形便簽反而是終端里那幾行命令。今天想聊的是我用 Python 自建一個命令行待辦事項應(yīng)用的完整過程從為什么選命令行到數(shù)據(jù)怎么存、命令怎么設(shè)計再到中間踩過的坑和最后沉淀下來的實用技巧。這類工具聽起來簡單仿佛寫個增刪改查就能交差但真正要把它做成一個自己愿意每天打開的東西需要考慮的細節(jié)遠比想象中多。1. 為什么圖形界面解決不了“終端前隨手記一筆”的需求你第一反應(yīng)可能是待辦事項手機備忘錄、各種日歷應(yīng)用、桌面小組件哪個不能做確實能但那是另一類使用場景。我自己的情況是大部分工作時間都在終端里很多待辦不是“下午四點開會”這種帶強時間屬性的事件而是“發(fā)版前記得改版本號”“修完這個 bug 后把日志加上”“測試環(huán)境更新之前先備份數(shù)據(jù)庫”這類和工作上下文強綁定的短任務(wù)。它們不需要鬧鐘提醒不需要分類成“個人”“工作”只需要在我掃一眼終端時立刻想起來哦手上還有這件事。這類場景放進圖形應(yīng)用里會產(chǎn)生一個摩擦你得先切走注意力打開另一個軟件再等它加載完。而命令行工具沒有這個過程。你就在終端里敲一行命令任務(wù)已經(jīng)記下來了。這種“零切換”的體驗恰恰是圖形界面替代不了的地方。1.1 這套工具真正要應(yīng)對的使用場景我最初觸發(fā)這個念頭是在一次線上問題排查之后。當時連續(xù)幾個小時都在命令行里查日志、改配置、重試操作中間接到幾件臨時任務(wù)確認某個服務(wù)的新版本號、讓同事提交一份接口變更說明、給某個目錄寫個清理腳本。如果用圖形待辦工具就意味著每來一件任務(wù)我都要從終端切出去記錄在那種爭分奪秒的現(xiàn)場這是不可接受的。類似場景還有很多。SSH 登錄遠程服務(wù)器做維護時你身邊往往沒有自己常用的圖形工具隨身帶著手機又覺得在服務(wù)器上做事卻要在手機上翻待辦很滑稽。再比如深夜處理告警大腦本來就在高速運轉(zhuǎn)如果記錄任務(wù)還要經(jīng)過“進入應(yīng)用、點擊新建、輸入標題、設(shè)置提醒”這套流程那和沒記錄沒有區(qū)別。還有一些使用人群不太一樣——不是每個人都每天打開十幾個圖形應(yīng)用。無論是寫腳本的運維、維護 CI/CD 的開發(fā)者還是長期待在終端里的數(shù)據(jù)分析師都需要一個“不會主動打擾、但隨叫隨到”的任務(wù)清單。命令行工具的優(yōu)勢就在這里它安靜地待在你的數(shù)據(jù)文件里你來了它就在你不來它也不刷存在感。1.2 命令行應(yīng)用不可替代的三個原因如果把這套工具和圖形待辦應(yīng)用放在一起對比命令行至少有三個地方是真正差異化的。第一是啟動速度和資源占用。圖形應(yīng)用啟動往往要經(jīng)歷窗口加載、界面渲染動輒一兩秒甚至更久很多還會常駐后臺進程沒事就在托盤里占著十幾 M 內(nèi)存。命令行工具是真正用完即走的每次執(zhí)行只存在幾百毫秒執(zhí)行完就退出不給你桌面環(huán)境平添負擔。第二是可腳本化。這是命令行工具“降維打擊”一般的能力。我可以寫一個定時任務(wù)每天早上自動把今天到期且未完成的任務(wù)列出來可以在發(fā)布流程里加一步彈出一個“記得更新 changelog”的提示甚至可以把任務(wù)輸出和 shell 腳本串聯(lián)做完一件事順手把對應(yīng)任務(wù)標記為完成。圖形應(yīng)用做得再精致也很難提供這種可編程接口。第三是數(shù)據(jù)透明。圖形待辦工具的數(shù)據(jù)往往存在各自的應(yīng)用沙盒里想看一眼還要專門找導(dǎo)出功能。命令行工具的數(shù)據(jù)就是普通文件你可以直接打開可以用其他腳本處理可以放進版本管理工具。這種透明性對個人數(shù)據(jù)來說是一種安全感——“我知道我的任務(wù)躺在哪里也知道它長什么樣子”。2. 技術(shù)選型Python 配 JSON 文件是怎樣權(quán)衡出來的工具定位決定了技術(shù)路線。它需要跨平臺、容易改、沒有太復(fù)雜的外部依賴同時因為我獨用不需要考慮多人并發(fā)和復(fù)雜權(quán)限。帶著這幾點約束我做了下面幾輪取舍。2.1 用 Python 而不是其他語言這里邊有哪些妥協(xié)最初的備選項其實不少Go、Rust、Node.js、Python 都寫過命令行工具我也并不排斥其中任何一個。最終選擇 Python 核心原因是標準庫足夠用。argparse、json、pathlib、time、tempfile、fcntl這些已經(jīng)覆蓋了 CLI 參數(shù)解析、數(shù)據(jù)讀寫、備份、鎖文件等幾乎全部需求不需要額外安裝第三方庫也就意味著在任何一臺裝有標準 Python 解釋器的機器上都能直接跑。這里當然有妥協(xié)。Python 的啟動速度比編譯型語言慢我實際測過進程從敲下回車到輸出結(jié)果大約 300 到 600 毫秒取決于系統(tǒng)環(huán)境。圖形應(yīng)用動輒幾秒的啟動時間讓我們習慣了等待但命令行工具連半秒都顯得拖沓。我曾在一次發(fā)布窗口里連續(xù)使用十幾個操作感受到那種“幾乎無感”的瞬時反饋有多重要。為了進一步壓縮啟動時間我也想過換成 Go但后來評估發(fā)現(xiàn)待辦事項應(yīng)用每次操作本來就少半秒的延遲換成開發(fā)效率和跨平臺免編譯的好處完全值得。如果哪天我真覺得這半秒不能忍了用 Go 重寫一遍也是同一個設(shè)計方案數(shù)據(jù)格式和命令接口不變遷移成本很低。另一點讓我對 Python 更放心的原因是它無需編譯。改一行字段校驗邏輯保存后馬上就能跑調(diào)試路徑足夠短。很多個人工具修改頻率高每改一次都要編譯部署的話會顯著降低你優(yōu)化它的意愿。2.2 存儲方案JSON 文件還是嵌入式數(shù)據(jù)庫這是做工具類應(yīng)用時繞不開的決策。下表是我在兩種方案之間反復(fù)對比的依據(jù)維度JSON 文件嵌入式數(shù)據(jù)庫查詢能力需要自己寫過濾邏輯但簡單任務(wù)足夠支持 SQL復(fù)雜聯(lián)表自然并發(fā)安全多進程同時寫會相互覆蓋需要鎖自帶事務(wù)與并發(fā)控制數(shù)據(jù)可讀性文本直接查看可用任何編輯器修改需要專門工具訪問依賴復(fù)雜度標準庫即可內(nèi)置為一個庫文件體積略大適合場景單用戶、任務(wù)量小、重可讀性多進程、數(shù)據(jù)量大、復(fù)雜查詢我選 JSON 文件的理由很直接待辦事項的數(shù)據(jù)結(jié)構(gòu)化程度低、數(shù)據(jù)量有限常見的過濾無非按狀態(tài)、按標簽、按優(yōu)先級這類操作用一次線性掃描就能輕松處理。而且 JSON 的透明性是無可替代的——如果哪天工具本身出了問題我還可以直接用任意編輯器把數(shù)據(jù)打開來修復(fù)這種“裸奔式”的可靠感對命令行用戶很重要。嵌入式數(shù)據(jù)庫固然更規(guī)范但對我這個項目來說明顯過重了。我不需要 LIMIT 分頁、不需要索引優(yōu)化、不需要跨進程事務(wù)硬要上一個數(shù)據(jù)庫反而破壞了“所見即所得”的直覺。選型原則應(yīng)該是用最簡單的能解決問題的方案而不是用功能最全的方案。后面加功能時如果發(fā)現(xiàn)單文件 JSON 撐不住了再換數(shù)據(jù)庫不遲。2.3 任務(wù)字段怎么設(shè)計才能兼顧簡單與好用數(shù)據(jù)結(jié)構(gòu)是這類應(yīng)用的地基我在第一次設(shè)計時沒想全后來付出了改腳本的代價。最終沉淀下來的任務(wù)字段大致如下{ id: a3f9c2e1, title: 發(fā)版前確認版本號已更新, description: , status: pending, priority: high, due_date: 2025-06-10, tags: [release, ops], created_at: 2025-06-08T09:30:00, completed_at: null }字段解釋一下id 是唯一標識用來精確操作某一條任務(wù)title 是任務(wù)標題description 是可選描述status 是狀態(tài)pending、in_progress、completed 三種后面細說priority 是高、中、低三檔due_date 是截止日期只存日期不存時間tags 是標簽數(shù)組created_at 和 completed_at 是時間戳。有幾個字段是后來才加的。description 原本沒有后來發(fā)現(xiàn)一些任務(wù)需要寫點背景比如“這個配置改過之后要回滾回滾步驟見某某鏈接”否則過兩周再看標題已經(jīng)想不起來原委。tags 也是實用主義的結(jié)果工作里的任務(wù)天然分成“項目”“運維”“個人”等維度光靠平鋪列表很難快速篩選。這些字段的教訓(xùn)是第一版不必貪多但至少要預(yù)留類型意識否則后面加字段就要寫遷移邏輯。3. 命令設(shè)計把常用操作收斂為一屏能看懂的命令集命令行應(yīng)用最忌諱的是命令名長得像繞口令或者每個操作都要求記住一長串參數(shù)。我設(shè)計命令時的原則很簡單高頻操作要短危險操作要多一步確認整個命令集一眼看過去不會超過一屏。3.1 完整命令集常用的 10 條操作下面是最終沉淀下來的命令總覽命令作用todo add 任務(wù)標題添加任務(wù)可加 --priority、--due、--tagtodo list列出全部任務(wù)可加 --status、--tag、--due-beforetodo show id查看單條任務(wù)的完整信息todo start id把任務(wù)狀態(tài)從 pending 改為 in_progresstodo done id標記為完成todo reopen id重新打開已完成任務(wù)todo update id --priority high更新任務(wù)屬性todo delete id刪除任務(wù)需要 --yes 確認todo stats統(tǒng)計未完成、已完成、過期任務(wù)數(shù)量todo backup手動備份數(shù)據(jù)文件這樣一個周期里最常用的操作除了刪除其余命令都在三個單詞以內(nèi)。我刻意沒有把“edit”做成全屏文本編輯器因為update 加參數(shù)的方式更可控也更容易在 shell 歷史里留下操作軌跡。3.2 參數(shù)解析里最容易出錯的地方講幾個真實的“坑”新手很容易踩。第一個是任務(wù)標題帶空格的問題。命令是todo add 這是一個任務(wù)如果用戶忘記加引號add后的位置參數(shù)只能拿到第一個單詞。我的解決方案是用argparse的REMAINDER模式讓add命令把剩余參數(shù)全部當作標題再配合--區(qū)隔選項這樣即使標題里夾著引號、特殊字符也不容易崩。第二個是日期格式的統(tǒng)一。這個極其重要。用戶今天輸入--due 明天明天輸入--due 2025-06-20如果每個別名都支持解析邏輯會越寫越復(fù)雜而且容易出現(xiàn)歧義。最終我把日期格式收斂為YYYY-MM-DD并在解析入口做一個純函數(shù)統(tǒng)一校驗。不支持的格式直接報錯絕不默認當成今天或任意猜測。原因是“猜錯了用戶可能完全注意不到”導(dǎo)致一個任務(wù)在錯誤的日期下被靜默過濾掉。第三個是危險操作的確認。刪除任務(wù)這種不可逆操作我在命令層做了二次確認不傳--yes就進入交互式確認如果確認發(fā)現(xiàn)傳了一個不存在的 id也要給出提示而不是默默忽略。CLI 工具做得越順手越要防“順手把重要數(shù)據(jù)刪了”的意外。3.3 核心命令的代碼實現(xiàn)骨架下面是核心數(shù)據(jù)層和 add、list 兩個命令的實現(xiàn)骨架其它命令邏輯大同小異。import argparse import datetime import json import os import tempfile import uuid DATA_FILE os.path.expanduser(~/.todo.json) def load_data(): try: with open(DATA_FILE, r, encodingutf-8) as f: data json.load(f) except FileNotFoundError: data {schema_version: 1, tasks: []} return data def save_data(data): dir_name os.path.dirname(os.path.abspath(DATA_FILE)) fd, tmp_path tempfile.mkstemp(dirdir_name) with os.fdopen(fd, w, encodingutf-8) as f: json.dump(data, f, indent2, ensure_asciiFalse) os.replace(tmp_path, DATA_FILE) def cmd_add(args, data): task { id: str(uuid.uuid4())[:8], title: args.title, description: args.description or , status: pending, priority: args.priority, due_date: args.due, tags: args.tags.split(,) if args.tags else [], created_at: datetime.datetime.now().isoformat(timespecseconds), completed_at: None, } data[tasks].append(task) save_data(data) print(f已添加任務(wù)ID: {task[id]}) def cmd_list(args, data): tasks data[tasks] priority_rank {high: 0, medium: 1, low: 2} tasks.sort(keylambda t: ( priority_rank.get(t.get(priority, medium), 1), t.get(due_date) or 9999-12-31 )) for t in tasks: status t[status] mark [完成] if status completed else [進行] if status in_progress else [待辦] print(f{t[id]} {mark} {t[title]} 優(yōu)先級:{t.get(priority)} 截止:{t.get(due_date)} 標簽:{,.join(t.get(tags, []))})代碼里最值得說的是排序邏輯。due_date可能為空排序時就把它映射成9999-12-31讓沒有截止日期的任務(wù)排到最后優(yōu)先級先于日期排列因為“重要且緊急”永遠應(yīng)該優(yōu)先于“單純明天到期”。粗看這像是數(shù)據(jù)細節(jié)其實它決定了列表的可用性。4. 讓任務(wù)真正“動”起來狀態(tài)機、過濾與視圖待辦事項應(yīng)用如果只做到增刪改查那本質(zhì)上就是個帶命令行外殼的文本編輯器。讓它成為“工具”的關(guān)鍵是把任務(wù)放進工作流里這就離不開狀態(tài)設(shè)計和過濾視圖。4.1 狀態(tài)機設(shè)計三個狀態(tài)背后的工作流思考最初的版本只有兩個狀態(tài)未完成和已完成。用了兩周之后發(fā)現(xiàn)不夠有些任務(wù)已經(jīng)開始處理了比如“修完 bug 后加日志”這件事我已經(jīng)改了兩行代碼但還沒完成下次打開列表時希望知道“這件事已經(jīng)進行中”而不想看到它和其他沒開始的雜事混在一起。于是加了in_progress狀態(tài)。加上之后任務(wù)的生命周期變得清晰當前狀態(tài)允許執(zhí)行的操作目標狀態(tài)pendingstartin_progresspendingdonecompletedin_progressdonecompletedin_progressreopenpendingcompletedreopenpending任意狀態(tài)delete刪除之所以可以通過pending直接done是因為現(xiàn)實中經(jīng)常出現(xiàn)“還沒來得及開始就完成了”的任務(wù)比如一個同事順手把事件處理掉了。強行要求必須經(jīng)過中間狀態(tài)會顯得死板。實現(xiàn)上我維護了一個合法的狀態(tài)轉(zhuǎn)換表非法操作直接報錯ALLOWED_TRANSITIONS { pending: [in_progress, completed], in_progress: [completed, pending], completed: [pending], } def transition(status, action): target {start: in_progress, done: completed, reopen: pending}[action] if target in ALLOWED_TRANSITIONS[status]: return target raise ValueError(f無法將狀態(tài)從 {status} 通過 {action} 變更為 {target})不要小看這一步它讓任務(wù)的流轉(zhuǎn)有了“語義”。當別人追問“這個任務(wù)現(xiàn)在到哪一步了”你能通過狀態(tài)一目了然地回答。沒有狀態(tài)機的數(shù)據(jù)倉庫只會在列表里堆出一堆永不更新的舊記錄。4.2 過濾、排序與視圖一個待辦清單的靈魂命令行的待辦清單最怕什么最怕輸出一堆讓人眼花繚亂的任務(wù)。任務(wù)一旦超過三十條不做過濾就是災(zāi)難。所以我給list命令加了幾個常用過濾參數(shù)todo list --status pending todo list --tag release todo list --due-before 2025-06-30 todo list --status in_progress --priority high過濾邏輯用一段樸素的循環(huán)實現(xiàn)各個條件之間是“與”的關(guān)系同時滿足才會保留def apply_filters(tasks, statusNone, tagNone, due_beforeNone): result [] for t in tasks: if status and t.get(status) ! status: continue if tag and tag not in t.get(tags, []): continue if due_before and (t.get(due_date) or 9999-12-31) due_before: continue result.append(t) return result過濾之外我還有一個個人偏好列表輸出不要超過 20 行。任務(wù)一多我就只會在意“今天最要緊的幾件事”所以把--limit 10直接設(shè)為隱含參數(shù)不傳也默認只顯示前 10 條想看全部再顯式加--limit 0。命令行應(yīng)用講究一個“克制”和“清晰”寧可少一些多余輸出也不要讓信息淹沒重點。這里有一個實用技巧把todo list的輸出格式處理成“ID 狀態(tài)標記 標題”的緊湊行每行不超過 80 個字符。這樣即使不做任何美化放在任何終端窗口都不會折行混亂。復(fù)雜展示放在todo show id里需要看完整信息時再展開。4.3 批量操作與安全護欄任務(wù)基礎(chǔ)少時逐條操作尚可任務(wù)積累多了以后批量操作幾乎是剛需。最常用的兩個是“一次性完成全部工作”和“清理已完成任務(wù)”todo done --all --status in_progress todo clean --done批量操作很容易誤傷所以我把安全護欄作為默認行為不支持“直接全部搞定”的語法必須加--status參數(shù)縮小范圍涉及批量刪除的命令第一次執(zhí)行時先輸出將受影響的任務(wù)數(shù)量再要求輸入y確認。有些人可能嫌這一步多余但我在一次誤操作后意識到批量操作是“手滑高危區(qū)”寧可多花一秒確認也不能用恢復(fù)數(shù)據(jù)的痛苦去冒險。還值得一提的細節(jié)是增加了--dry-run選項。讓批量操作先走一遍完整邏輯只打印將執(zhí)行的動作不真正改動數(shù)據(jù)。這讓我可以在執(zhí)行前快速掃一眼結(jié)果確認無誤后再去掉--dry-run重新執(zhí)行。命令行工具想做得好用“安全”和“快捷”本來就要這樣反復(fù)平衡。5. 數(shù)據(jù)安全與格式兼容命令行工具更該怕丟數(shù)據(jù)圖形應(yīng)用丟數(shù)據(jù)你會怪它自己寫的命令行工具丟數(shù)據(jù)就沒處甩鍋了。數(shù)據(jù)文件是單文件 JSON結(jié)構(gòu)簡單但簡單文件的脆弱性也正是它的軟肋。寫入一半斷電、多終端并發(fā)寫、格式版本變化這些都有可能造成災(zāi)難。我在這個項目上做的安全措施對任何單文件數(shù)據(jù)存儲都值得借鑒。5.1 原子寫入避免一次崩潰毀掉全部任務(wù)最常見的損壞場景是程序打開數(shù)據(jù)文件寫入了一部分此時突然斷電或進程被殺文件停留在半截狀態(tài)。直接后果是 JSON 解析失敗整個任務(wù)列表打不開。使用with open(DATA_FILE, w)這種直接覆蓋寫法風險敞口非常大。正確做法是“原子寫入”先寫到同目錄下的臨時文件再通過os.replace原子替換舊文件。同目錄很重要因為跨文件系統(tǒng)移動不一定具備原子性而在同一文件系統(tǒng)內(nèi)os.replace是原子的。代碼就像前面 save_data 展示的那樣先mkstemp再os.replace這樣任何時刻目標路徑要么是舊文件、要么是新文件絕不會是半截狀態(tài)。我把崩潰恢復(fù)也考慮進去了即使寫入臨時文件后、替換前進程被殺舊文件也不會損壞唯一殘留只是一個.tmp文件下次啟動時清理掉即可。這段保護邏輯代碼量不多卻讓數(shù)據(jù)的可靠性提升了一個檔次。5.2 多終端同時打開時的并發(fā)與鎖使用場景繞不開“多個終端同時操作”。比如工作機上開著兩個 shell或者用 SSH 從另一臺機器訪問兩邊同時各跑一條todo done。如果兩邊都讀到舊數(shù)據(jù)、各自修改后再寫回會出現(xiàn)經(jīng)典的“后寫覆蓋先寫”問題——一個任務(wù)標記完成了另一個終端又把它還原了。解決辦法也不復(fù)雜在 Linux/macOS 上用fcntl對數(shù)據(jù)文件加鎖鎖持有期間才允許做加載-修改-寫回操作在 Windows 上則退化為樸素的“先清臨時文件再直接寫”因為fcntl不是標準庫。簡單起見我給工具加了一個啟動鎖文件機制核心代碼如下import fcntl LOCK_FILE os.path.expanduser(~/.todo.lock) def acquire_lock(): f open(LOCK_FILE, w) fcntl.flock(f, fcntl.LOCK_EX) return f def main(): lock acquire_lock() data load_data() # 執(zhí)行命令邏輯 save_data(data) fcntl.flock(lock, fcntl.LOCK_UN)當然鎖只能約束使用同一個工具的進程。如果你用編輯器直接修改 JSON 文件又正好和工具寫回并發(fā)操作依然可能沖突這個只能靠使用習慣回避。但工具之間的相互覆蓋問題用鎖已經(jīng)可以杜絕了。5.3 數(shù)據(jù)遷移、備份與向前兼容我在load_data里留了schema_version字段這個字段到現(xiàn)在已經(jīng)救過我一兩次了。每版腳本如果字段結(jié)構(gòu)發(fā)生變化就在數(shù)據(jù)文件加版本號加載的時候根據(jù)版本號走對應(yīng)的遷移函數(shù)而不是直接按當前代碼期望的結(jié)構(gòu)解析。字段一旦不匹配程序要么報錯要么悄悄產(chǎn)生空值都會造成數(shù)據(jù)污染。備份則是一個獨立的todo backup命令它的實現(xiàn)是復(fù)制當前數(shù)據(jù)文件為帶時間戳的.bak文件todo backup # 生成 ~/.todo.20250608-1530.bak說實話這個命令我平時很少主動想起來用所以后來干脆在每次“批量刪除”之前自動跑一次備份。這樣即便確認時手滑選中了不該刪的任務(wù)也還有上次操作前的完整快照。命令行工具的意義不是讓你更膽大而是讓你在犯錯之后有退路。6. 連續(xù)使用一段時間后的真實體驗與擴展方向工具做完以后我把它裝進了自己的日常工作流現(xiàn)在幾乎每天都在用。這一章不寫教程式的收尾只記錄幾段真實使用感受以及這個項目未來可能延伸出的方向。6.1 日常使用方式與真實感受我現(xiàn)在最典型的使用節(jié)奏是早上到工位先打開終端跑一遍todo list --status pending --due-before today看一眼今天必須處理的事。然后整天的雜事都往里面塞有的任務(wù)連一個描述都沒有只有一句話標題比如“恢復(fù)某服務(wù)自動更新”。處理到一半的會在狀態(tài)上標成進行中靠todo list --status in_progress把握手頭節(jié)奏。每次進入代碼庫之前我也會用todo add把腦子里想到的收尾工作記下來避免寫著寫著把“記得補充注釋”這條細節(jié)徹底忘掉。這種“寫到一半隨時記一筆”的場景圖形應(yīng)用做不到這么好因為切走的瞬間注意力就斷了。真實感受中最重要的一點是這個工具因為“太簡陋”反而沒有心理負擔。圖形待辦應(yīng)用功能多但正經(jīng)事沒記住多少而命令行工具只記任務(wù)本身連打開它都覺得輕快所以記錄黏性反而很高。這也印證了我最初的觀點工具不應(yīng)該用功能數(shù)量來評價而要看它能不能融入你的使用習慣。6.2 現(xiàn)有方案的明顯短板必須誠實地說這個工具目前還有幾個短板。第一是沒有真正的“提醒”能力我只能靠每次list看到過期任務(wù)被動提醒無法在任務(wù)到期時主動通知。解決方案是外面套一個 crontab 任務(wù)定期運行但這樣也沒法做到像手機推送那樣實時。第二是移動端場景完全空白如果出門在外想添加一個任務(wù)只能通過遠程終端這對沒有隨身帶電腦習慣的人很不方便。第三是交互式編輯缺失想改一長串描述時update的參數(shù)方式遠不如一個編輯器來的高效。這幾塊短板讓我意識到命令行工具適合做“個人快速記錄和信息維持”但不適合做“全天候提醒中心”。做好定位比追求功能大而全更實際。6.3 再往下走這個項目可以長成什么樣如果繼續(xù)迭代第一批我會做提醒與過期掃描。在list輸出中把過期的待辦任務(wù)前置并且用顯眼的字符標記“已過期”再加一個todo today命令只顯示今天到期或已過期的任務(wù)。這能提高每天開始的效率。第二批是子任務(wù)和重復(fù)任務(wù)。子任務(wù)滿足“一個大任務(wù)拆成幾個小步驟”的天然分解需求重復(fù)任務(wù)解決“每天早上更新環(huán)境”這類周期性事項。這兩個功能都會讓任務(wù)模型復(fù)雜化需要小心控制擴展范圍。第三批是統(tǒng)計與導(dǎo)出。比如每周自動輸出一份任務(wù)完成率報告或者導(dǎo)出成 iCal 格式與日歷應(yīng)用打通。這塊是錦上添花把一個至少“能記錄”的工具變成“能復(fù)盤”的數(shù)據(jù)源。在我個人的實踐里命令行待辦事項應(yīng)用最值得推薦的部分其實不是某個具體功能而是一種“以小事撬動日常效率”的做法。先動手做一個最小可用的版本放進真實使用中再讓它沿著自己的習慣慢慢成長。所謂效率工具從來不是安裝一個別人的完美作品而是長成你自己用得最順手的模樣。