命令行程序边界
命令行程序通过参数、环境变量、标准流、文件和退出状态与外界交互。请将每一项都视为公共接口,因为其他程序可能会依赖这些行为。
标准流
stdin:输入数据。stdout:预期的结果输出。stderr:诊断信息和进度提示,不应干扰管道处理。
input() 从 stdin 读取一行;只有提供提示参数时才会先打印提示符。它适用于交互式工具,但不适合数据管道。处理流式输入时,直接遍历 sys.stdin;面向用户的输出使用 print,运行时的操作日志使用 logging。
import sys
for line in sys.stdin:
sys.stdout.write(line.upper())
如果输出需要被机器解析,请保持纯净,不要混入标题、颜色代码或临时日志。当脚本需要消费结果时,提供独立的 JSON 等结构化模式。
参数与配置
使用 argparse 而非手动解析 sys.argv:
import argparse
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("input", type=Path)
parser.add_argument("--format", choices=("text", "json"), default="text")
args = parser.parse_args()
参数是显式的,会出现在进程列表和 Shell 历史中,因此不适合传递敏感信息。环境变量适合传递继承的部署配置,但它们本质上是字符串,可能通过进程检查或诊断日志泄露,并非安全的密钥存储。
import os
endpoint = os.environ.get("APP_ENDPOINT", "https://example.invalid")
在程序启动阶段一次性解析并验证配置。如果业务逻辑依赖此区别,需区分“变量未设置”和“变量值为空”。严禁在库代码深处修改进程级环境变量。
退出状态
成功返回 0,失败返回有文档记录的非零状态。使用 main 函数可以让这一契约变得可测试:
def main() -> int:
...
return 0
if __name__ == "__main__":
raise SystemExit(main())
异常是内部控制流机制;退出状态和 stderr 才是对外的 CLI 契约。仅在边界处捕获异常,将其转化为有用的诊断信息和稳定的退出码。保留足够的调试上下文,但避免打印敏感信息或完整的敏感载荷。