🚀

用 Python + SQLite 搭一个代码片段管理器,告别满屏找代码

本地存储、标签分类、全文搜索——50 行 Python 代码实现一个轻量级代码片段管理工具,再也不用在文件夹里翻来翻去。

👁️ - 次阅读

你是不是也有这种经历?

写了一段很棒的正则表达式,三个月后想复用,死活找不到在哪。或者同事分享了一个优雅的排序算法,你随手贴进备忘录,结果再也没打开过。

我就是这样。后来决定自己写一个代码片段管理器。

不用数据库,不用前端框架。Python + SQLite,50 多行代码搞定。

项目背景

程序员每天产生大量代码片段:一段 SQL 查询、一个 CSS 技巧、一个 API 调用模板。它们散落在笔记软件、浏览器书签、甚至聊天窗口里。

需要一个工具,能同时满足这几个条件:

  • 本地存储,不依赖网络
  • 全文搜索,输入关键词就能找到对应代码
  • 标签分类,按语言或用途分组
  • 零配置,打开就用

市面上有 SnippetBox、GistHub 之类的工具,但要么太重,要么要注册账号。我自己写的这个,跑在本机上,数据完全掌控在自己手里。

技术选型

|| 组件 | 选择 | 理由 | |——|——|——| | 存储 | SQLite3 | Python 内置,零安装 | | 搜索 | Python in 操作符 | 简单直接,够用 | | 格式化 | Pygments | 语法高亮,可选 | | 交互 | argparse + input() | 命令行工具,轻量 |

SQLite 是最佳选择。它不是一个独立的数据库服务,就是一个文件。你的代码片段存在一个 .db 文件里,拷贝走就行。

实现步骤

第一步:初始化数据库

# snippet_manager.py
import sqlite3
import os
from datetime import datetime

DB_PATH = os.path.expanduser("~/.code_snippets.db")

def init_db():
    """初始化数据库表"""
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS snippets (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            content TEXT NOT NULL,
            language TEXT DEFAULT 'text',
            tags TEXT DEFAULT '',
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    """)
    
    cursor.execute("""
        CREATE INDEX IF NOT EXISTS idx_title ON snippets(title);
        CREATE INDEX IF NOT EXISTS idx_tags ON snippets(tags);
        CREATE INDEX IF NOT EXISTS idx_content ON snippets(content);
    """)
    
    conn.commit()
    return conn

建了三张索引。标题、标签、内容都建立索引,这样搜索的时候不会太慢。

第二步:添加和保存片段

def add_snippet(conn, title, content, language="text", tags=""):
    """添加一个新的代码片段"""
    cursor = conn.cursor()
    cursor.execute(
        """INSERT INTO snippets (title, content, language, tags) 
           VALUES (?, ?, ?, ?)""",
        (title, content, language, tags)
    )
    conn.commit()
    print(f"✅ 已保存: {title} (ID: {cursor.lastrowid})")
    return cursor.lastrowid


def update_snippet(conn, snippet_id, **kwargs):
    """更新已有片段"""
    fields = []
    values = []
    for key, value in kwargs.items():
        fields.append(f"{key} = ?")
        values.append(value)
    fields.append("updated_at = CURRENT_TIMESTAMP")
    values.append(snippet_id)
    
    query = f"UPDATE snippets SET {', '.join(fields)} WHERE id = ?"
    conn.execute(query, values)
    conn.commit()
    print(f"✅ 已更新: ID {snippet_id}")

保存片段只需要传标题、内容和语言类型。标签用逗号分隔的字符串存就行,简单粗暴但有效。

第三步:搜索和列出片段

def search_snippets(conn, keyword, limit=20):
    """全文搜索代码片段"""
    cursor = conn.cursor()
    # 在标题、内容、标签三个字段中搜索
    query = """
        SELECT id, title, language, tags, created_at,
               substr(content, 1, 200) as preview
        FROM snippets
        WHERE title LIKE ? OR content LIKE ? OR tags LIKE ?
        ORDER BY updated_at DESC
        LIMIT ?
    """
    pattern = f"%{keyword}%"
    cursor.execute(query, (pattern, pattern, pattern, limit))
    return cursor.fetchall()


def list_snippets(conn, language=None, tag=None, limit=50):
    """列出所有片段,支持按语言/标签过滤"""
    query = "SELECT id, title, language, tags, created_at FROM snippets WHERE 1=1"
    params = []
    
    if language:
        query += " AND language = ?"
        params.append(language)
    if tag:
        query += " AND tags LIKE ?"
        params.append(f"%{tag}%")
    
    query += " ORDER BY updated_at DESC LIMIT ?"
    params.append(limit)
    
    return conn.execute(query, params).fetchall()


def get_snippet(conn, snippet_id):
    """获取单个片段的完整内容"""
    row = conn.execute(
        "SELECT * FROM snippets WHERE id = ?", (snippet_id,)
    ).fetchone()
    if row:
        print(f"\n标题: {row[1]}")
        print(f"语言: {row[3] or 'text'}")
        print(f"标签: {row[4] or '(无)'}")
        print(f"创建: {row[5]}")
        print("-" * 50)
        print(row[2])  # 完整内容
        print("-" * 50)
    else:
        print(f"❌ 未找到 ID 为 {snippet_id} 的片段")

搜索逻辑很简单:用 LIKE %keyword% 在标题、内容和标签里做模糊匹配。三个字段一起搜,命中率更高。

第四步:命令行交互界面

def main():
    import sys
    
    conn = init_db()
    
    if len(sys.argv) < 2:
        print("用法:")
        print("  python snippet_manager.py add \"标题\" \"内容\" [语言] [标签]")
        print("  python snippet_manager.py search \"关键词\"")
        print("  python snippet_manager.py list [--lang python]")
        print("  python snippet_manager.py show <ID>")
        print("  python snippet_manager.py delete <ID>")
        return
    
    cmd = sys.argv[1]
    
    if cmd == "add":
        if len(sys.argv) < 4:
            print("❌ 请提供标题和内容")
            return
        title = sys.argv[2]
        # 剩余参数合并为内容(支持多行)
        content = " ".join(sys.argv[3:-2]) if len(sys.argv) > 4 else sys.argv[3]
        language = sys.argv[-2] if len(sys.argv) > 4 and not sys.argv[-2].startswith('"') else "text"
        tags = sys.argv[-1] if len(sys.argv) > 5 else ""
        add_snippet(conn, title, content, language, tags)
    
    elif cmd == "search":
        if len(sys.argv) < 3:
            print("❌ 请提供搜索关键词")
            return
        results = search_snippets(conn, sys.argv[2])
        if not results:
            print("🔍 未找到匹配结果")
            return
        print(f"\n找到 {len(results)} 条结果:\n")
        for row in results:
            print(f"  [{row[0]}] {row[1]} ({row[2] or 'text'}) — {row[4]}")
            print(f"      {row[5]}...")
            print()
    
    elif cmd == "list":
        lang_filter = None
        tag_filter = None
        args = sys.argv[2:]
        i = 0
        while i < len(args):
            if args[i] == "--lang" and i + 1 < len(args):
                lang_filter = args[i + 1]
                i += 2
            elif args[i] == "--tag" and i + 1 < len(args):
                tag_filter = args[i + 1]
                i += 2
            else:
                i += 1
        results = list_snippets(conn, lang_filter, tag_filter)
        print(f"\n{len(results)} 个片段:\n")
        for row in results:
            print(f"  [{row[0]}] {row[1]} | {row[2] or 'text'} | {row[3] or '-'}")
    
    elif cmd == "show":
        if len(sys.argv) < 3:
            print("❌ 请提供片段 ID")
            return
        get_snippet(conn, int(sys.argv[2]))
    
    elif cmd == "delete":
        if len(sys.argv) < 3:
            print("❌ 请提供片段 ID")
            return
        conn.execute("DELETE FROM snippets WHERE id = ?", (int(sys.argv[2]),))
        conn.commit()
        print(f"✅ 已删除 ID {sys.argv[2]}")
    
    conn.close()

if __name__ == "__main__":
    main()

第五步:使用示例

# 添加一个 Python 片段
python snippet_manager.py add "列表推导式过滤" \
    "[x for x in data if x > 0]" python "python,基础"

# 添加一个 SQL 片段
python snippet_manager.py add "分页查询模板" \
    "SELECT * FROM users LIMIT 20 OFFSET 0;" sql "sql,数据库"

# 搜索包含 "filter" 的代码
python snippet_manager.py search filter

# 按语言列出所有 Python 片段
python snippet_manager.py list --lang python

# 查看完整内容
python snippet_manager.py show 1

# 删除一个片段
python snippet_manager.py delete 1

输出效果:

找到 2 条结果:

  [1] 列表推导式过滤 (python) — 2026-07-15 14:30:00
      [x for x in data if x > 0]...

  [3] 正则提取邮箱 (python) — 2026-07-14 09:15:00
      re.findall(r'\b[\w.-]+@[\w.-]+\.\w+\b', text)...

运行效果

装好之后,命令行里直接跑。数据存在 ~/.code_snippets.db 里,一个文件搞定。

你可以把这段代码加到你的 .bashrc 里做个别名:

alias sm='python /path/to/snippet_manager.py'

然后 sm search "正则" 就能快速查找。

优化方向

  1. JSON 导出:把片段导出成 JSON 格式,方便备份和迁移到其他平台
  2. Markdown 预览:支持在片段里写 Markdown 说明文档
  3. Web 界面:用 Flask 加个简单的 Web UI,手机也能查
  4. 同步功能:配合 git 或 syncthing 在多设备间同步数据库文件
  5. 语法高亮:集成 Pygments,在终端里显示彩色代码
  6. 版本历史:记录每次修改,支持回滚到旧版本

代码片段就像积木。攒得多了,写新东西的时候直接拿来拼,速度翻倍。

你的代码库里,现在最缺的是哪一类片段?去翻翻旧项目,说不定就能补上一块。

这篇文章对你有帮助吗?