Viết công cụ dòng lệnh bằng Python với argparse: quản lý ghi chú ngay trong terminal

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Nhiều công cụ lập trình viên dùng hằng ngày như git, npm, docker đều là chương trình dòng lệnh. Python có sẵn module argparse giúp bạn tự viết công cụ như vậy rất nhanh. Trong bài này, bạn sẽ làm một công cụ ghi chú với các lệnh thêm, xem, hoàn thành và xóa.

Thiết kế giao diện lệnh

Trước khi viết, hãy hình dung cách dùng. Ta muốn gõ như sau trong terminal:

python notes.py add "Mua sữa"
python notes.py list
python notes.py done 1
python notes.py remove 1

Cấu trúc này gồm một chương trình chính và nhiều lệnh con (subcommand), tương tự git commit hay git push. Argparse hỗ trợ điều này qua add_subparsers.

Bước 1: Hàm lưu và đọc dữ liệu

Ghi chú được lưu vào một file JSON ở thư mục chạy chương trình. Cách đơn giản và dễ kiểm tra bằng mắt.

import json
from pathlib import Path

DATA_FILE = Path("notes.json")

def load_notes():
    if not DATA_FILE.exists():
        return []
    with DATA_FILE.open(encoding="utf-8") as f:
        return json.load(f)

def save_notes(notes):
    with DATA_FILE.open("w", encoding="utf-8") as f:
        json.dump(notes, f, ensure_ascii=False, indent=2)

Tùy chọn ensure_ascii=False giữ nguyên chữ tiếng Việt có dấu thay vì chuyển thành mã thoát khó đọc.

Bước 2: Các hàm xử lý lệnh

def cmd_add(args):
    notes = load_notes()
    next_id = max((n["id"] for n in notes), default=0) + 1
    notes.append({"id": next_id, "text": args.text, "done": False})
    save_notes(notes)
    print(f"Đã thêm ghi chú #{next_id}")

def cmd_list(args):
    notes = load_notes()
    if not args.all:
        notes = [n for n in notes if not n["done"]]
    if not notes:
        print("Không có ghi chú nào.")
        return
    for n in notes:
        mark = "x" if n["done"] else " "
        print(f"[{mark}] {n['id']:>3}. {n['text']}")

def cmd_done(args):
    notes = load_notes()
    for n in notes:
        if n["id"] == args.id:
            n["done"] = True
            save_notes(notes)
            print(f"Đã hoàn thành #{args.id}")
            return
    print(f"Không tìm thấy ghi chú #{args.id}")

def cmd_remove(args):
    notes = load_notes()
    remaining = [n for n in notes if n["id"] != args.id]
    if len(remaining) == len(notes):
        print(f"Không tìm thấy ghi chú #{args.id}")
        return
    save_notes(remaining)
    print(f"Đã xóa #{args.id}")

Để ý cách tính mã mới bằng max(..., default=0) + 1: tham số default tránh lỗi khi danh sách rỗng, và việc không dùng độ dài danh sách giúp mã không bị trùng sau khi xóa.

Bước 3: Khai báo parser

import argparse

def build_parser():
    parser = argparse.ArgumentParser(
        prog="notes",
        description="Công cụ ghi chú trên dòng lệnh",
    )
    sub = parser.add_subparsers(dest="command", required=True)

    p_add = sub.add_parser("add", help="thêm ghi chú mới")
    p_add.add_argument("text", help="nội dung ghi chú")
    p_add.set_defaults(func=cmd_add)

    p_list = sub.add_parser("list", help="xem danh sách")
    p_list.add_argument("-a", "--all", action="store_true",
                        help="hiện cả ghi chú đã hoàn thành")
    p_list.set_defaults(func=cmd_list)

    p_done = sub.add_parser("done", help="đánh dấu hoàn thành")
    p_done.add_argument("id", type=int, help="mã ghi chú")
    p_done.set_defaults(func=cmd_done)

    p_rm = sub.add_parser("remove", help="xóa ghi chú")
    p_rm.add_argument("id", type=int, help="mã ghi chú")
    p_rm.set_defaults(func=cmd_remove)

    return parser

def main():
    parser = build_parser()
    args = parser.parse_args()
    args.func(args)

if __name__ == "__main__":
    main()

Mỗi lệnh con đăng ký hàm xử lý bằng set_defaults(func=...), nên hàm main chỉ cần gọi args.func(args). Đây là mẫu gọn gàng tránh chuỗi if/elif dài. Tham số type=int tự chuyển đổi và báo lỗi nếu người dùng gõ chữ. Cờ action="store_true" tạo công tắc bật tắt như --all.

Thử chạy và xem trợ giúp

Argparse tự sinh thông báo trợ giúp. Hãy chạy thử:

python notes.py --help
python notes.py add --help
python notes.py add "Học argparse"
python notes.py list
python notes.py done 1
python notes.py list --all

Làm cho công cụ chuyên nghiệp hơn

  • Lưu dữ liệu ở thư mục người dùng (ví dụ dưới Path.home()) thay vì thư mục hiện tại.
  • Trả mã thoát khác 0 khi lỗi bằng sys.exit(1) để script khác phát hiện được.
  • Dùng thư viện rich để in bảng màu đẹp, hoặc click/typer nếu muốn cú pháp khai báo gọn hơn.
  • Đóng gói bằng pyproject.toml với mục entry point để gõ notes thay vì python notes.py.
  • Viết kiểm thử cho các hàm bằng pytest, dùng thư mục tạm để không đụng dữ liệu thật.

Kết luận

Argparse cho bạn phân tích tham số, kiểm tra kiểu và tự sinh trợ giúp mà không cần cài thêm gì. Viết công cụ dòng lệnh nhỏ là cách tuyệt vời để tự động hóa việc lặp lại hằng ngày và luyện tư duy chia hàm.

Để học Python từ cơ bản đến ứng dụng thực tế, bạn có thể khám phá các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment