argparse 是 Python 標準庫中用於建立命令列介面(CLI)的模組。它能自動生成幫助訊息、處理各種參數類型,並進行輸入驗證。

基本用法

最簡單的 CLI

1import argparse
2
3parser = argparse.ArgumentParser(description="我的程式")
4parser.add_argument("filename", help="要處理的檔案")
5args = parser.parse_args()
6
7print(f"處理檔案: {args.filename}")

執行:

 1$ python script.py myfile.txt
 2處理檔案: myfile.txt
 3
 4$ python script.py --help
 5usage: script.py [-h] filename
 6
 7我的程式
 8
 9positional arguments:
10  filename    要處理的檔案
11
12options:
13  -h, --help  show this help message and exit

參數類型

位置參數(Positional Arguments)

必須提供的參數:

1parser.add_argument("filename")
2# 使用: python script.py myfile.txt

可選參數(Optional Arguments)

使用 --- 開頭:

1parser.add_argument("-v", "--verbose", action="store_true")
2parser.add_argument("-o", "--output", default="output.txt")
3# 使用: python script.py -v -o result.txt

布林旗標

1# store_true:出現時為 True
2parser.add_argument("--debug", action="store_true")
3
4# store_false:出現時為 False
5parser.add_argument("--no-cache", action="store_false", dest="cache")

實際範例:Hook 驗證器

來自 .claude/lib/hook_validator.py

 1def main():
 2    """命令行介面"""
 3    parser = argparse.ArgumentParser(
 4        description="Hook 合規性驗證工具",
 5        formatter_class=argparse.RawDescriptionHelpFormatter,
 6        epilog="""
 7使用範例:
 8  # 驗證單一 Hook
 9  python hook_validator.py .claude/hooks/my-hook.py
10
11  # 驗證所有 Hook
12  python hook_validator.py --all
13
14  # 輸出 JSON 格式
15  python hook_validator.py --all --json
16
17  # 自訂 Hook 目錄
18  python hook_validator.py --all --dir .claude/hooks
19        """
20    )
21
22    parser.add_argument(
23        "hook_path",
24        nargs="?",
25        help="Hook 檔案路徑(相對或絕對)"
26    )
27    parser.add_argument(
28        "--all",
29        action="store_true",
30        help="驗證所有 Hook 檔案"
31    )
32    parser.add_argument(
33        "--dir",
34        help="自訂 Hook 目錄路徑(預設 .claude/hooks)"
35    )
36    parser.add_argument(
37        "--json",
38        action="store_true",
39        help="輸出 JSON 格式"
40    )
41    parser.add_argument(
42        "--strict",
43        action="store_true",
44        help="嚴格模式:將 warning 視為 error"
45    )
46
47    args = parser.parse_args()
48
49    # 根據參數執行不同邏輯
50    if args.all:
51        results = validate_all_hooks(hooks_dir=args.dir)
52    elif args.hook_path:
53        results = [validate_hook(args.hook_path)]
54    else:
55        parser.print_help()
56        sys.exit(1)
57
58    # 輸出結果
59    if args.json:
60        print(json.dumps(output, ensure_ascii=False, indent=2))
61    else:
62        print(format_validation_report(results))

常用參數選項

nargs - 參數數量

 1# 單一值(預設)
 2parser.add_argument("filename")
 3
 4# 可選(0 或 1)
 5parser.add_argument("output", nargs="?", default="out.txt")
 6
 7# 零或多個
 8parser.add_argument("files", nargs="*")
 9
10# 一或多個
11parser.add_argument("files", nargs="+")
12
13# 固定數量
14parser.add_argument("point", nargs=2, type=int)  # 需要兩個整數

type - 型別轉換

1# 整數
2parser.add_argument("--count", type=int, default=10)
3
4# 浮點數
5parser.add_argument("--ratio", type=float)
6
7# 檔案路徑
8from pathlib import Path
9parser.add_argument("--config", type=Path)

choices - 限制選項

1parser.add_argument(
2    "--format",
3    choices=["json", "yaml", "text"],
4    default="text",
5    help="輸出格式"
6)

default - 預設值

1parser.add_argument("--timeout", type=int, default=30)
2parser.add_argument("--verbose", action="store_true")  # 預設 False

required - 強制必填

1parser.add_argument("--config", required=True)

dest - 屬性名稱

1parser.add_argument("--no-cache", action="store_false", dest="use_cache")
2# args.use_cache 而非 args.no_cache

實際範例:Markdown 連結檢查器

來自 .claude/lib/markdown_link_checker.py

 1def main():
 2    """命令行介面"""
 3    parser = argparse.ArgumentParser(
 4        description="Markdown 連結檢查工具",
 5        formatter_class=argparse.RawDescriptionHelpFormatter,
 6        epilog="""
 7使用範例:
 8  # 檢查單一文件
 9  python markdown_link_checker.py docs/README.md
10
11  # 檢查整個目錄
12  python markdown_link_checker.py --dir .claude/methodologies/
13
14  # JSON 輸出
15  python markdown_link_checker.py --dir docs/ --json
16
17  # 只檢查當前目錄(不遞迴)
18  python markdown_link_checker.py --dir docs/ --no-recursive
19        """
20    )
21
22    parser.add_argument(
23        "file_path",
24        nargs="?",
25        help="Markdown 檔案路徑"
26    )
27    parser.add_argument(
28        "--dir",
29        help="要檢查的目錄路徑"
30    )
31    parser.add_argument(
32        "--json",
33        action="store_true",
34        help="輸出 JSON 格式"
35    )
36    parser.add_argument(
37        "--no-recursive",
38        action="store_true",
39        help="不遞迴檢查子目錄"
40    )
41
42    args = parser.parse_args()
43
44    # 決定工作模式
45    if args.dir:
46        results = checker.check_directory(
47            args.dir,
48            recursive=not args.no_recursive
49        )
50    elif args.file_path:
51        results = [checker.check_file(args.file_path)]
52    else:
53        parser.print_help()
54        sys.exit(1)

進階技巧

參數群組

1parser = argparse.ArgumentParser()
2
3# 必要參數群組
4required = parser.add_argument_group("required arguments")
5required.add_argument("--config", required=True)
6
7# 可選參數群組
8optional = parser.add_argument_group("optional arguments")
9optional.add_argument("--verbose", action="store_true")

互斥參數

1group = parser.add_mutually_exclusive_group()
2group.add_argument("--json", action="store_true")
3group.add_argument("--yaml", action="store_true")
4# 只能選擇其中一個

子命令

 1parser = argparse.ArgumentParser()
 2subparsers = parser.add_subparsers(dest="command")
 3
 4# add 子命令
 5add_parser = subparsers.add_parser("add", help="新增項目")
 6add_parser.add_argument("name")
 7
 8# list 子命令
 9list_parser = subparsers.add_parser("list", help="列出項目")
10list_parser.add_argument("--all", action="store_true")
11
12args = parser.parse_args()
13if args.command == "add":
14    # 處理 add
15    pass
16elif args.command == "list":
17    # 處理 list
18    pass

完整範例模板

 1#!/usr/bin/env python3
 2"""
 3我的 CLI 工具
 4
 5使用方式:
 6    python my_tool.py input.txt -o output.txt --verbose
 7"""
 8
 9import argparse
10import sys
11
12
13def create_parser() -> argparse.ArgumentParser:
14    """建立參數解析器"""
15    parser = argparse.ArgumentParser(
16        description="我的 CLI 工具",
17        formatter_class=argparse.RawDescriptionHelpFormatter,
18        epilog="範例: python my_tool.py input.txt -o output.txt"
19    )
20
21    parser.add_argument(
22        "input",
23        help="輸入檔案"
24    )
25    parser.add_argument(
26        "-o", "--output",
27        default="output.txt",
28        help="輸出檔案 (預設: output.txt)"
29    )
30    parser.add_argument(
31        "-v", "--verbose",
32        action="store_true",
33        help="詳細輸出"
34    )
35    parser.add_argument(
36        "--version",
37        action="version",
38        version="%(prog)s 1.0.0"
39    )
40
41    return parser
42
43
44def main():
45    parser = create_parser()
46    args = parser.parse_args()
47
48    if args.verbose:
49        print(f"Input: {args.input}")
50        print(f"Output: {args.output}")
51
52    # 主要邏輯
53    process(args.input, args.output)
54
55
56if __name__ == "__main__":
57    main()

最佳實踐

1. 提供有意義的 help 訊息

1parser.add_argument(
2    "--timeout",
3    type=int,
4    default=30,
5    help="超時時間(秒),預設 30"  # 說明用途和預設值
6)

2. 使用 epilog 提供使用範例

1parser = argparse.ArgumentParser(
2    epilog="""
3範例:
4  %(prog)s file.txt                    # 處理單一檔案
5  %(prog)s -d ./data --recursive       # 遞迴處理目錄
6    """
7)

3. 合理的 exit code

1if not results:
2    sys.exit(0)  # 成功
3else:
4    sys.exit(1)  # 失敗

思考題

  1. nargs="?"nargs="*" 有什麼區別?
  2. 為什麼 --no-recursive 使用 action="store_true" 而不是 store_false
  3. 如何實作一個同時支援 --verbose-v 的參數?

實作練習

  1. 為現有的 Python 腳本添加 CLI 介面
  2. 實作一個支援子命令的 CLI 工具
  3. 建立一個參數驗證函式,檢查檔案是否存在

上一章:logging - 日誌系統 下一模組:物件導向設計