本文 AI 產出,尚未審核

Python – 自動化與腳本應用(Automation & CLI)

主題:argparseclick 模組


簡介

在日常的開發與維運工作中,命令列介面 (CLI) 是最常見的自動化入口。無論是資料處理、測試腳本,或是部署工具,都需要透過參數、選項與子指令讓使用者靈活控制程式行為。Python 內建的 argparse 與第三方的 click,是兩套最受歡迎的 CLI 建構套件。

  • argparse 於 Python 2.7 之後即成標準庫,適合不想額外安裝套件且需求相對簡單的情境。
  • click 則以「Command Line Interface Creation Kit」為名,提供更直覺的裝飾器語法、子指令支援以及自動產生的說明文件,常被用於較大型的工具或開源專案。

掌握這兩個模組的 核心概念常見陷阱最佳實踐,能讓你的腳本從「只能跑」升級為「好用又易維護」的專業工具。


核心概念

1. argparse 的基本流程

  1. 建立 ArgumentParser 物件:設定程式說明、預設的錯誤處理方式。
  2. 定義參數:使用 add_argument() 加入位置參數、選項參數或旗標。
  3. 解析參數:呼叫 parse_args(),返回一個屬性化的 Namespace 物件。

範例 1:最簡單的「Hello」程式

import argparse

parser = argparse.ArgumentParser(description="簡易問候程式")
parser.add_argument("name", help="要問候的對象")
args = parser.parse_args()

print(f"Hello, {args.name}!")
  • name位置參數,必須出現在指令的最後。
  • 執行 python hello.py Alice 後會輸出 Hello, Alice!

範例 2:加入選項與預設值

import argparse

parser = argparse.ArgumentParser(description="計算兩數之和")
parser.add_argument("a", type=int, help="第一個整數")
parser.add_argument("b", type=int, help="第二個整數")
parser.add_argument("-v", "--verbose", action="store_true",
                    help="顯示詳細計算過程")
args = parser.parse_args()

result = args.a + args.b
if args.verbose:
    print(f"{args.a} + {args.b} = {result}")
else:
    print(result)
  • -v/--verbose旗標,使用 store_true 讓它在出現時回傳 True
  • type=intargparse 自動把字串轉成整數,若轉換失敗會顯示友善的錯誤訊息。

範例 3:支援子指令(sub‑commands)

import argparse

parser = argparse.ArgumentParser(description="檔案管理小工具")
subparsers = parser.add_subparsers(dest="command", required=True)

# copy 子指令
cp_parser = subparsers.add_parser("copy", help="複製檔案")
cp_parser.add_argument("src", help="來源檔案")
cp_parser.add_argument("dst", help="目標檔案")

# delete 子指令
rm_parser = subparsers.add_parser("delete", help="刪除檔案")
rm_parser.add_argument("target", help="要刪除的檔案")
rm_parser.add_argument("-f", "--force", action="store_true",
                       help="強制刪除,不詢問")

args = parser.parse_args()

if args.command == "copy":
    print(f"Copy {args.src} → {args.dst}")
elif args.command == "delete":
    print(f"Delete {args.target}, force={args.force}")
  • add_subparsers() 讓程式支援 多個子指令,每個子指令都有自己的參數集合。
  • dest="command" 把子指令名稱存入 args.commandrequired=True 確保必須指定子指令。

2. click 的設計哲學

click裝飾器(decorator) 為核心,將 CLI 定義與函式本身緊密結合,讓程式碼更具可讀性。它的特點包括:

  • 自動產生 --help:不需要額外撰寫說明文件。
  • 支援型別轉換、預設值與回呼(callback)
  • 支援多層子指令,且可以輕鬆加入 環境變數設定檔 等進階功能。

範例 4:最基礎的 Click 程式

import click

@click.command()
@click.argument('name')
def hello(name):
    """簡易問候程式"""
    click.echo(f'Hello, {name}!')

if __name__ == '__main__':
    hello()
  • @click.command() 宣告這是一個 CLI 命令。
  • @click.argument('name') 定義位置參數。
  • click.echo() 取代內建的 print(),自動處理 Unicode 與流向。

範例 5:選項、型別與預設值

import click

@click.command()
@click.option('-a', '--a', type=int, required=True, help='第一個整數')
@click.option('-b', '--b', type=int, required=True, help='第二個整數')
@click.option('-v', '--verbose', is_flag=True, help='顯示詳細資訊')
def add(a, b, verbose):
    """計算兩數之和"""
    result = a + b
    if verbose:
        click.echo(f'{a} + {b} = {result}')
    else:
        click.echo(result)

if __name__ == '__main__':
    add()
  • is_flag=True--verbose 成為布林旗標。
  • type=int 直接在 Click 內完成型別轉換,若錯誤會自動拋出 BadParameter

範例 6:多層子指令(Group)

import click
import shutil
import os

@click.group()
def cli():
    """檔案管理小工具"""
    pass

@cli.command()
@click.argument('src', type=click.Path(exists=True))
@click.argument('dst', type=click.Path())
def copy(src, dst):
    """複製檔案"""
    shutil.copy(src, dst)
    click.echo(f'Copy {src} → {dst}')

@cli.command()
@click.argument('target', type=click.Path(exists=True))
@click.option('-f', '--force', is_flag=True, help='強制刪除')
def delete(target, force):
    """刪除檔案"""
    if not force and not click.confirm(f'確定要刪除 {target}?'):
        click.echo('已取消')
        return
    os.remove(target)
    click.echo(f'Deleted {target}')

if __name__ == '__main__':
    cli()
  • @click.group() 建立一個 指令群組,子指令使用 @cli.command() 註冊。
  • click.Path() 可檢查檔案是否存在、是否為檔案或目錄,減少手動驗證的程式碼。

常見陷阱與最佳實踐

陷阱 可能的結果 解決方式 / 最佳實踐
混用 argparseclick 兩套解析器會爭奪 sys.argv,導致錯誤或無法產生 --help 選擇其一:若專案已大量使用 click,就全程使用;若只需要簡單參數,直接使用 argparse
忘記設定 required=True(在 argparse 位置參數缺失時會產生不易理解的 SystemExit 明確標示必填參數,或自行檢查 None 後給予友善訊息。
過度使用 store_true 而忽略預設值argparse 使用者未提供旗標時,程式無法辨識「未設定」與「設定為 False」的差異。 若需要三態(True / False / None),改用 action='store_const' 或自行處理。
子指令名稱衝突click 兩個子指令同名會在執行時覆蓋,造成不可預期行為。 @click.group() 中使用 invoke_without_command=True,或在命名時加入前綴。
未處理例外 當使用者輸入不合法的路徑或型別時,腳本直接崩潰。 使用 try/except 包住關鍵區塊,並利用 click.ClickExceptionargparse.ArgumentTypeError 提供清晰訊息。
說明文件過長、未對齊 --help 輸出難以閱讀。 使用 formatter_class=argparse.ArgumentDefaultsHelpFormatterargparse)或 help_option_names=['-h', '--help']click)統一格式。

最佳實踐總結

  1. 先規劃參數結構:先畫出需求圖,決定是否需要子指令、旗標或環境變數。
  2. 盡量使用型別與預設值:讓解析器自動驗證,減少手動檢查。
  3. 保持說明文件一致argparse 可使用 descriptionepilogclick 可在 @click.command(help=...) 中寫。
  4. 測試 CLI:利用 subprocess.run([...])click.testing.CliRunner 撰寫單元測試,確保參數變更不會破壞相容性。
  5. 考慮未來擴充:若預期會加入更多子指令,建議直接使用 click,因為其群組機制較為彈性。

實際應用場景

場景 推薦模組 為什麼
一次性資料清理腳本 argparse 需求簡單、只需幾個位置參數或旗標,無需額外依賴。
CI/CD 工具(如自動部署、測試) click 多子指令(buildtestdeploy)與豐富的選項、環境變數支援,使腳本可成為完整的 CLI 套件。
系統管理員的批次作業 click + click.Path 自動檢查檔案/目錄、提供互動式確認,降低誤操作風險。
教學或範例程式 argparse 內建模組,讓學員免安裝外部套件即可直接執行。
跨平台工具(Windows、Linux、macOS) 任意,視需求而定;若要支援 自動補全(bash/zsh),click 提供 click_completion 擴充套件。

總結

  • argparse標準庫,適合輕量、一次性或教學用的 CLI;其核心概念是 建立解析器 → 定義參數 → 解析
  • click裝飾器 為核心,提供更直覺的子指令管理、型別驗證與自動化說明文件,特別適合 中大型工具
  • 兩者的 共同要點:明確的參數規劃、充分的說明文件、妥善的錯誤處理與單元測試,都是打造 好用且可靠 CLI 的關鍵。

掌握上述概念與實務技巧後,你就能根據專案規模與需求,靈活選擇 argparseclick,讓 Python 腳本從「只能執行」升級為「使用者友好、易於維護」的專業自動化工具。祝你寫出更多得心應手的命令列程式!