本文 AI 產出,尚未審核

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

主題:sys.argv


簡介

在日常開發與系統管理工作中,命令列介面(CLI) 是最常見的自動化入口。無論是批次處理、資料轉換、或是快速測試小工具,都會透過指令列傳遞參數給程式。Python 內建的 sys 模組提供了 argv(argument vector)屬性,讓我們能夠直接取得使用者在呼叫腳本時所帶入的參數。

掌握 sys.argv 的用法,不只可以讓腳本變得更彈性,也能提升 可重用性可測試性,甚至在 CI/CD 流程中自動化執行測試或部署指令。這篇文章將一步步帶你了解 sys.argv 的運作原理、常見寫法、實務範例,並分享避免踩坑的最佳實踐。


核心概念

1. sys.argv 是什麼?

  • sys.argv 是一個 list(列表),其中的每個元素都是字串(str),代表執行腳本時的參數。
  • 第 0 個元素(sys.argv[0])永遠是 腳本本身的檔名(或完整路徑),後續的元素則是使用者輸入的參數。
# example.py
import sys
print(sys.argv)

執行 python example.py hello world 後的輸出:

['example.py', 'hello', 'world']

小提醒:在 Windows、macOS、Linux 上的行為一致,唯一差異是路徑分隔符號。


2. 參數類型與轉型

sys.argv 取得的都是字串,若需要數值或布林值,必須自行 轉型(type conversion)。常見的做法是使用 int()float() 或自行寫判斷函式。

# convert.py
import sys

# 假設指令列: python convert.py 12 3.14 true
num_int   = int(sys.argv[1])          # 12
num_float = float(sys.argv[2])        # 3.14
flag      = sys.argv[3].lower() == 'true'  # True
print(num_int, num_float, flag)

3. 基本的錯誤處理

使用者可能忘記帶參數或帶入錯誤格式,直接存取 sys.argv[1] 會拋出 IndexError。最簡單的防護方式是先檢查 長度,或使用 try/except 包住轉型程式。

# safe.py
import sys

if len(sys.argv) < 2:
    print("Usage: python safe.py <number>")
    sys.exit(1)   # 結束程式,回傳錯誤碼 1

try:
    value = int(sys.argv[1])
except ValueError:
    print("Error: the argument must be an integer.")
    sys.exit(2)

print(f"Your number is {value}")

4. 解析較複雜的參數:argparse vs 手寫

對於簡單的腳本,直接使用 sys.argv 足以;但當參數變多、需要支援 選項(option)旗標(flag)子指令 時,建議改用標準庫的 argparse。以下示範兩者的差異:

4.1 手寫解析(適合 1~2 個參數)

# hello.py
import sys

if len(sys.argv) != 2:
    print("Usage: python hello.py <name>")
    sys.exit(1)

name = sys.argv[1]
print(f"Hello, {name}!")

4.2 使用 argparse(適合多參數、選項)

# greet.py
import argparse

parser = argparse.ArgumentParser(description="Greet someone.")
parser.add_argument("name", help="The person's name")
parser.add_argument("-t", "--times", type=int, default=1,
                    help="How many times to greet")
args = parser.parse_args()

for _ in range(args.times):
    print(f"Hello, {args.name}!")

執行 python greet.py Alice -t 3

Hello, Alice!
Hello, Alice!
Hello, Alice!

5. 取得完整的指令列字串

有時候想保留原始的指令列(包含空格、引號),可以使用 sys.argv 重新組合,或直接讀取 sys.argv 前的 shell 變數。以下示範把參數重新拼回一個字串:

# raw_cmd.py
import sys

raw = " ".join(sys.argv)
print(f"Raw command line: {raw}")

程式碼範例(實用示例)

下面提供 5 個 常見且實用的範例,從最簡單的參數讀取到稍微進階的檔案批次處理。

範例 1:簡易的加法計算機

# add.py
import sys

if len(sys.argv) != 3:
    print("Usage: python add.py <num1> <num2>")
    sys.exit(1)

try:
    a = float(sys.argv[1])
    b = float(sys.argv[2])
except ValueError:
    print("Error: both arguments must be numbers.")
    sys.exit(2)

print(f"{a} + {b} = {a + b}")

說明:使用 float 讓使用者可以輸入整數或小數,並示範基本的錯誤處理。


範例 2:批次改名工具(rename files)

# batch_rename.py
import sys
import os

if len(sys.argv) != 3:
    print("Usage: python batch_rename.py <directory> <prefix>")
    sys.exit(1)

directory = sys.argv[1]
prefix    = sys.argv[2]

if not os.path.isdir(directory):
    print(f"Error: '{directory}' is not a valid folder.")
    sys.exit(2)

for idx, filename in enumerate(os.listdir(directory), start=1):
    src = os.path.join(directory, filename)
    if os.path.isfile(src):
        ext = os.path.splitext(filename)[1]
        dst = os.path.join(directory, f"{prefix}_{idx}{ext}")
        os.rename(src, dst)
        print(f"Renamed: {filename} -> {prefix}_{idx}{ext}")

說明:透過 sys.argv 取得目錄與前綴字,示範 檔案系統操作os 模組的結合使用。


範例 3:讀取 CSV 並根據參數過濾

# filter_csv.py
import sys
import csv

if len(sys.argv) != 4:
    print("Usage: python filter_csv.py <csv_file> <column_name> <value>")
    sys.exit(1)

csv_path, column, value = sys.argv[1], sys.argv[2], sys.argv[3]

with open(csv_path, newline='', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    matches = [row for row in reader if row.get(column) == value]

print(f"Found {len(matches)} rows where {column} == {value}")
for row in matches[:10]:   # 只顯示前 10 筆
    print(row)

說明:結合 csv.DictReader,展示 資料處理sys.argv 的實務應用。


範例 4:簡易的 HTTP GET 請求(使用 requests

# get_url.py
import sys
import requests

if len(sys.argv) != 2:
    print("Usage: python get_url.py <url>")
    sys.exit(1)

url = sys.argv[1]

try:
    resp = requests.get(url, timeout=5)
    resp.raise_for_status()
except requests.RequestException as e:
    print(f"Request failed: {e}")
    sys.exit(2)

print(f"Status: {resp.status_code}")
print("Headers:")
for k, v in resp.headers.items():
    print(f"  {k}: {v}")
print("\nBody (first 200 chars):")
print(resp.text[:200])

說明:示範外部套件的使用,說明 例外捕捉(exception handling)與參數驗證。


範例 5:使用 argparse 實作子指令(備份與還原)

# backup_tool.py
import argparse
import shutil
import os
import sys

def backup(src, dst):
    if not os.path.isdir(src):
        print(f"Error: source '{src}' is not a folder.")
        sys.exit(1)
    shutil.make_archive(dst, 'zip', src)
    print(f"Backup completed: {dst}.zip")

def restore(archive, dst):
    if not os.path.isfile(archive):
        print(f"Error: archive '{archive}' not found.")
        sys.exit(1)
    shutil.unpack_archive(archive, dst)
    print(f"Restore completed to: {dst}")

parser = argparse.ArgumentParser(description="Simple backup/restore tool")
subparsers = parser.add_subparsers(dest='command', required=True)

# backup subcommand
p_backup = subparsers.add_parser('backup', help='Create a zip backup')
p_backup.add_argument('src', help='Source directory')
p_backup.add_argument('dst', help='Destination file name (without .zip)')

# restore subcommand
p_restore = subparsers.add_parser('restore', help='Restore from a zip archive')
p_restore.add_argument('archive', help='Backup zip file')
p_restore.add_argument('dst', help='Destination directory')

args = parser.parse_args()

if args.command == 'backup':
    backup(args.src, args.dst)
elif args.command == 'restore':
    restore(args.archive, args.dst)

說明:結合 argparse子指令(sub‑command)概念,讓腳本同時支援多功能,提升可維護性。


常見陷阱與最佳實踐

陷阱 為什麼會發生 解決方式
IndexError:直接存取 sys.argv[1] 而未檢查長度 使用者忘記帶參數 先檢查 len(sys.argv),或使用 try/except
類型錯誤:把字串直接當成數字使用 sys.argv 只回傳字串 使用 int()float(),必要時捕捉 ValueError
平台差異:Windows 路徑中會出現反斜線 \ 字串處理未考慮斜線 使用 os.pathpathlib 進行路徑操作
引號與空格:參數內含空格時被切割 未在命令列加上引號 執行時使用 " " 包住參數,例如 python script.py "Hello World"
安全性:直接把參數傳給 os.systemsubprocess 可能造成 命令注入 使用 subprocess.run([...], check=True) 並避免直接拼接字串

最佳實踐

  1. 盡量使用 argparse:它會自動產生說明訊息(--help)與錯誤處理,讓使用者體驗更好。
  2. 把參數解析與業務邏輯分離:將 parse_args() 放在 if __name__ == "__main__": 區塊,主函式只負責實作功能。
  3. 加入說明與範例:使用 parser.descriptionparser.epilog,提供使用者快速上手的範例。
  4. 回傳適當的退出碼sys.exit(0) 表示成功,非 0 數字代表不同錯誤類型,方便 CI/CD 判斷。
  5. 撰寫單元測試:利用 unittest.mock.patch('sys.argv', [...]) 模擬不同參數情境,確保腳本在各種輸入下都能正常運作。

實際應用場景

場景 需求 sys.argv/argparse 的使用方式
資料清理 每日自動執行 CSV 清理腳本,接受日期參數 python clean.py 2025-11-20 → 讀取 sys.argv[1] 作為日期過濾條件
部署腳本 依環境(dev / staging / prod)傳入不同設定檔 python deploy.py --env prod --config prod.yaml(使用 argparse
批次影像處理 讀取資料夾路徑與輸出尺寸 python resize.py ./raw_images 800x600
系統監控 以腳本檢查服務狀態,接受服務名稱作為參數 python check_service.py nginxsys.argv[1] 為服務名稱
自動化測試 CI pipeline 中呼叫測試腳本,傳入測試套件名稱 python run_tests.py unitpython run_tests.py integration

總結

sys.argv 是 Python 與 命令列 互動的第一道門檻,掌握它能讓腳本快速接受外部參數、變得更具彈性。對於 簡單工具,直接使用 sys.argv 足以完成需求;當腳本功能逐漸複雜,建議升級到 argparse,享受自動產生說明、型別驗證與子指令等便利功能。

在實作過程中,別忘了:

  • 檢查參數長度,避免 IndexError
  • 轉型前捕捉 ValueError,確保輸入符合預期。
  • 使用 os.path / pathlib 處理檔案路徑,兼容不同作業系統。
  • 提供友善的使用說明--help),讓使用者不必翻閱程式碼。
  • 回傳適當的退出碼,方便自動化流程判斷成功或失敗。

透過本文的概念與範例,你已經具備了在日常自動化、資料處理、或是系統管理腳本中使用 sys.argv(或 argparse)的完整能力。快把學到的技巧運用到自己的專案中,讓 Python 成為你最得力的 CLI 工具吧!