用 Python 写一个顺手的命令行小工具

argparse 其实没那么难,难的是想清楚“这个工具应该有多小”。

我每天都会做一件很无聊的事:把临时想到的句子、读到的段落、待办事项,追加到同一个 Markdown 文件里。用编辑器当然可以,但每次都要切窗口、找文件、滚到底部,久而久之就不想记了。

于是写了一个十几行的命令,叫 note。

先把需求缩小

一开始我想得很复杂:分类、搜索、同步、导出……最后只留下一个动作:

Bash
note 今天读完了《CSS 世界》第三章 -t 阅读 -t css

它做三件事:把内容拼成一行、加上标签、追加到 notes.md。不做的部分同样重要:不建数据库、不联网、不弹提示。

用 argparse 描述接口

Python
from __future__ import annotations

import argparse
from pathlib import Path


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="note",
        description="把零散的想法追加到一个 Markdown 文件里。",
    )
    parser.add_argument("text", nargs="*", help="要记录的内容")
    parser.add_argument("-t", "--tag", action="append", default=[], metavar="TAG",
                        help="标签,可以重复使用")
    parser.add_argument("-f", "--file", type=Path, default=Path("notes.md"),
                        metavar="PATH", help="目标文件,默认 ./notes.md")
    return parser


def main() -> None:
    args = build_parser().parse_args()
    line = " ".join(args.text).strip()
    if not line:
        raise SystemExit("没有内容可记,试试:note 今天天气很好")

    if not args.file.parent.exists():
        args.file.parent.mkdir(parents=True, exist_ok=True)

    tags = " ".join(f"#{tag.lstrip('#')}" for tag in args.tag)
    with args.file.open("a", encoding="utf-8") as handle:
        handle.write(f"- {line} {tags}".rstrip() + "\n")


if __name__ == "__main__":
    main()

action="append" 让 -t 可以重复出现,type=Path 直接把字符串转成路径对象,nargs="*" 则允许内容里带空格而不用加引号。

让 --help 说人话

argparse 生成的帮助文本默认很干。加三样东西就够了:

  • prog:显示命令名,而不是 __main__.py。
  • description:一句话说清它是干什么的。
  • 每个参数的 help 与 metavar:让占位符看起来像人话。
文本
usage: note [-h] [-t TAG] [-f PATH] [text ...]

把零散的想法追加到一个 Markdown 文件里。

positional arguments:
  text                  要记录的内容

options:
  -h, --help            show this help message and exit
  -t TAG, --tag TAG     标签,可以重复使用
  -f PATH, --file PATH  目标文件,默认 ./notes.md

让它可以被直接调用

把文件放到 ~/bin/note(或任何在 PATH 里的目录),在开头加上 #!/usr/bin/env python3,然后赋予执行权限:

Bash
chmod +x ~/bin/note

Windows 上可以把它放进一个 .cmd 包装器,或者干脆用 python note.py 的别名。我用的是 PowerShell 的 profile:

PowerShell
function note { python "$HOME\tools\note.py" @args }

几条自己总结的规则

  1. 参数不超过五个。 超过就说明这个工具承担了太多职责,该拆了。
  2. 默认值要安全。 默认写入 notes.md,而不是覆盖某个已有文件。
  3. 输出要能接管道。 需要列出内容时,一行一条,不加装饰性字符。
  4. 错误信息要给出下一步。 与其说“参数错误”,不如说“没有内容可记,试试:note 今天天气很好”。

最后

这个工具现在只有二十来行,但我每天都会用。它没有配置文件,没有插件系统,也没有 README——因为使用方式就写在 --help 里。

写小工具最快乐的时刻,是发现自己已经想不起来没有它的时候是怎么干活的。

搜索文章