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.path 或 pathlib 進行路徑操作 |
| 引號與空格:參數內含空格時被切割 | 未在命令列加上引號 | 執行時使用 " " 包住參數,例如 python script.py "Hello World" |
安全性:直接把參數傳給 os.system、subprocess |
可能造成 命令注入 | 使用 subprocess.run([...], check=True) 並避免直接拼接字串 |
最佳實踐
- 盡量使用
argparse:它會自動產生說明訊息(--help)與錯誤處理,讓使用者體驗更好。 - 把參數解析與業務邏輯分離:將
parse_args()放在if __name__ == "__main__":區塊,主函式只負責實作功能。 - 加入說明與範例:使用
parser.description、parser.epilog,提供使用者快速上手的範例。 - 回傳適當的退出碼:
sys.exit(0)表示成功,非 0 數字代表不同錯誤類型,方便 CI/CD 判斷。 - 撰寫單元測試:利用
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 nginx → sys.argv[1] 為服務名稱 |
| 自動化測試 | CI pipeline 中呼叫測試腳本,傳入測試套件名稱 | python run_tests.py unit 或 python run_tests.py integration |
總結
sys.argv 是 Python 與 命令列 互動的第一道門檻,掌握它能讓腳本快速接受外部參數、變得更具彈性。對於 簡單工具,直接使用 sys.argv 足以完成需求;當腳本功能逐漸複雜,建議升級到 argparse,享受自動產生說明、型別驗證與子指令等便利功能。
在實作過程中,別忘了:
- 檢查參數長度,避免
IndexError。 - 轉型前捕捉
ValueError,確保輸入符合預期。 - 使用
os.path/pathlib處理檔案路徑,兼容不同作業系統。 - 提供友善的使用說明(
--help),讓使用者不必翻閱程式碼。 - 回傳適當的退出碼,方便自動化流程判斷成功或失敗。
透過本文的概念與範例,你已經具備了在日常自動化、資料處理、或是系統管理腳本中使用 sys.argv(或 argparse)的完整能力。快把學到的技巧運用到自己的專案中,讓 Python 成為你最得力的 CLI 工具吧!